58e35d87def4c2da258392e0f343dfd339e1f2e4
[jra/samba/.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 "lib/socket/socket.h"
24 #include "system/network.h"
25 #include "system/filesys.h"
26
27 static struct in6_addr interpret_addr6(const char *name)
28 {
29         struct hostent *he;
30         
31         if (name == NULL) return in6addr_any;
32         
33         he = gethostbyname2(name, PF_INET6);
34
35         if (he == NULL) return in6addr_any;
36
37         return *((struct in6_addr *)he->h_addr);
38 }
39
40 static NTSTATUS ipv6_tcp_init(struct socket_context *sock)
41 {
42         sock->fd = socket(PF_INET6, SOCK_STREAM, 0);
43         if (sock->fd == -1) {
44                 return map_nt_error_from_unix(errno);
45         }
46
47         return NT_STATUS_OK;
48 }
49
50 static void ipv6_tcp_close(struct socket_context *sock)
51 {
52         close(sock->fd);
53 }
54
55 static NTSTATUS ipv6_tcp_connect_complete(struct socket_context *sock, uint32_t flags)
56 {
57         int error=0, ret;
58         socklen_t len = sizeof(error);
59
60         /* check for any errors that may have occurred - this is needed
61            for non-blocking connect */
62         ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
63         if (ret == -1) {
64                 return map_nt_error_from_unix(errno);
65         }
66         if (error != 0) {
67                 return map_nt_error_from_unix(error);
68         }
69
70         if (!(flags & SOCKET_FLAG_BLOCK)) {
71                 ret = set_blocking(sock->fd, False);
72                 if (ret == -1) {
73                         return map_nt_error_from_unix(errno);
74                 }
75         }
76
77         sock->state = SOCKET_STATE_CLIENT_CONNECTED;
78
79         return NT_STATUS_OK;
80 }
81
82 static NTSTATUS ipv6_tcp_connect(struct socket_context *sock,
83                                  const char *my_address, int my_port,
84                                  const char *srv_address, int srv_port,
85                                  uint32_t flags)
86 {
87         struct sockaddr_in6 srv_addr;
88         struct in6_addr my_ip;
89         struct in6_addr srv_ip;
90         int ret;
91
92         my_ip = interpret_addr6(my_address);
93
94         if (memcmp(&my_ip, &in6addr_any, sizeof(my_ip)) || my_port != 0) {
95                 struct sockaddr_in6 my_addr;
96                 ZERO_STRUCT(my_addr);
97                 my_addr.sin6_addr       = my_ip;
98                 my_addr.sin6_port       = htons(my_port);
99                 my_addr.sin6_family     = PF_INET6;
100                 
101                 ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
102                 if (ret == -1) {
103                         return map_nt_error_from_unix(errno);
104                 }
105         }
106
107         srv_ip = interpret_addr6(srv_address);
108         if (memcmp(&srv_ip, &in6addr_any, sizeof(srv_ip)) == 0) {
109                 return NT_STATUS_BAD_NETWORK_NAME;
110         }
111
112         ZERO_STRUCT(srv_addr);
113         srv_addr.sin6_addr      = srv_ip;
114         srv_addr.sin6_port      = htons(srv_port);
115         srv_addr.sin6_family    = PF_INET6;
116
117         ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
118         if (ret == -1) {
119                 return map_nt_error_from_unix(errno);
120         }
121
122         return ipv6_tcp_connect_complete(sock, flags);
123 }
124
125 static NTSTATUS ipv6_tcp_listen(struct socket_context *sock,
126                                         const char *my_address, int port,
127                                         int queue_size, uint32_t flags)
128 {
129         struct sockaddr_in6 my_addr;
130         struct in6_addr ip_addr;
131         int ret;
132
133         socket_set_option(sock, "SO_REUSEADDR=1", NULL);
134
135         ip_addr = interpret_addr6(my_address);
136
137         ZERO_STRUCT(my_addr);
138         my_addr.sin6_addr       = ip_addr;
139         my_addr.sin6_port       = htons(port);
140         my_addr.sin6_family     = PF_INET6;
141
142         ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
143         if (ret == -1) {
144                 return map_nt_error_from_unix(errno);
145         }
146
147         ret = listen(sock->fd, queue_size);
148         if (ret == -1) {
149                 return map_nt_error_from_unix(errno);
150         }
151
152         if (!(flags & SOCKET_FLAG_BLOCK)) {
153                 ret = set_blocking(sock->fd, False);
154                 if (ret == -1) {
155                         return map_nt_error_from_unix(errno);
156                 }
157         }
158
159         sock->state= SOCKET_STATE_SERVER_LISTEN;
160
161         return NT_STATUS_OK;
162 }
163
164 static NTSTATUS ipv6_tcp_accept(struct socket_context *sock, struct socket_context **new_sock)
165 {
166         struct sockaddr_in cli_addr;
167         socklen_t cli_addr_len = sizeof(cli_addr);
168         int new_fd;
169
170         new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
171         if (new_fd == -1) {
172                 return map_nt_error_from_unix(errno);
173         }
174
175         if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
176                 int ret = set_blocking(new_fd, False);
177                 if (ret == -1) {
178                         close(new_fd);
179                         return map_nt_error_from_unix(errno);
180                 }
181         }
182
183         /* TODO: we could add a 'accept_check' hook here
184          *       which get the black/white lists via socket_set_accept_filter()
185          *       or something like that
186          *       --metze
187          */
188
189         (*new_sock) = talloc(NULL, struct socket_context);
190         if (!(*new_sock)) {
191                 close(new_fd);
192                 return NT_STATUS_NO_MEMORY;
193         }
194
195         /* copy the socket_context */
196         (*new_sock)->type               = sock->type;
197         (*new_sock)->state              = SOCKET_STATE_SERVER_CONNECTED;
198         (*new_sock)->flags              = sock->flags;
199
200         (*new_sock)->fd                 = new_fd;
201
202         (*new_sock)->private_data       = NULL;
203         (*new_sock)->ops                = sock->ops;
204
205         return NT_STATUS_OK;
206 }
207
208 static NTSTATUS ipv6_tcp_recv(struct socket_context *sock, void *buf, 
209                               size_t wantlen, size_t *nread, uint32_t flags)
210 {
211         ssize_t gotlen;
212         int flgs = 0;
213
214         /* TODO: we need to map all flags here */
215         if (flags & SOCKET_FLAG_PEEK) {
216                 flgs |= MSG_PEEK;
217         }
218
219         if (flags & SOCKET_FLAG_BLOCK) {
220                 flgs |= MSG_WAITALL;
221         }
222
223         *nread = 0;
224
225         gotlen = recv(sock->fd, buf, wantlen, flgs);
226         if (gotlen == 0) {
227                 return NT_STATUS_END_OF_FILE;
228         } else if (gotlen == -1) {
229                 return map_nt_error_from_unix(errno);
230         }
231
232         *nread = gotlen;
233
234         return NT_STATUS_OK;
235 }
236
237 static NTSTATUS ipv6_tcp_send(struct socket_context *sock, 
238                               const DATA_BLOB *blob, size_t *sendlen, uint32_t flags)
239 {
240         ssize_t len;
241         int flgs = 0;
242
243         *sendlen = 0;
244
245         len = send(sock->fd, blob->data, blob->length, flgs);
246         if (len == -1) {
247                 return map_nt_error_from_unix(errno);
248         }       
249
250         *sendlen = len;
251
252         return NT_STATUS_OK;
253 }
254
255 static NTSTATUS ipv6_tcp_set_option(struct socket_context *sock, const char *option, const char *val)
256 {
257         set_socket_options(sock->fd, option);
258         return NT_STATUS_OK;
259 }
260
261 static char *ipv6_tcp_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
262 {
263         struct sockaddr_in6 peer_addr;
264         socklen_t len = sizeof(peer_addr);
265         struct hostent *he;
266         int ret;
267
268         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
269         if (ret == -1) {
270                 return NULL;
271         }
272
273         he = gethostbyaddr((char *)&peer_addr.sin6_addr, sizeof(peer_addr.sin6_addr), AF_INET6);
274         if (he == NULL) {
275                 return NULL;
276         }
277
278         return talloc_strdup(mem_ctx, he->h_name);
279 }
280
281 static char *ipv6_tcp_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
282 {
283         struct sockaddr_in6 peer_addr;
284         socklen_t len = sizeof(peer_addr);
285         int ret;
286         struct hostent *he;
287
288         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
289         if (ret == -1) {
290                 return NULL;
291         }
292
293         he = gethostbyaddr((char *)&peer_addr.sin6_addr, sizeof(peer_addr.sin6_addr), AF_INET6);
294
295         if (!he || !he->h_name) {
296                 return NULL;
297         }
298         
299         return talloc_strdup(mem_ctx, he->h_name);
300 }
301
302 static int ipv6_tcp_get_peer_port(struct socket_context *sock)
303 {
304         struct sockaddr_in6 peer_addr;
305         socklen_t len = sizeof(peer_addr);
306         int ret;
307
308         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
309         if (ret == -1) {
310                 return -1;
311         }
312
313         return ntohs(peer_addr.sin6_port);
314 }
315
316 static char *ipv6_tcp_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
317 {
318         struct sockaddr_in6 my_addr;
319         socklen_t len = sizeof(my_addr);
320         int ret;
321         struct hostent *he;
322
323         ret = getsockname(sock->fd, (struct sockaddr *)&my_addr, &len);
324         if (ret == -1) {
325                 return NULL;
326         }
327
328         he = gethostbyaddr((char *)&my_addr.sin6_addr, sizeof(my_addr.sin6_addr), AF_INET6);
329         if (he == NULL) {
330                 return NULL;
331         }
332
333         return talloc_strdup(mem_ctx, he->h_name);
334 }
335
336 static int ipv6_tcp_get_my_port(struct socket_context *sock)
337 {
338         struct sockaddr_in6 my_addr;
339         socklen_t len = sizeof(my_addr);
340         int ret;
341
342         ret = getsockname(sock->fd, (struct sockaddr *)&my_addr, &len);
343         if (ret == -1) {
344                 return -1;
345         }
346
347         return ntohs(my_addr.sin6_port);
348 }
349
350 static int ipv6_tcp_get_fd(struct socket_context *sock)
351 {
352         return sock->fd;
353 }
354
355 static const struct socket_ops ipv6_tcp_ops = {
356         .name                   = "ipv6",
357         .fn_init                = ipv6_tcp_init,
358         .fn_connect             = ipv6_tcp_connect,
359         .fn_connect_complete    = ipv6_tcp_connect_complete,
360         .fn_listen              = ipv6_tcp_listen,
361         .fn_accept              = ipv6_tcp_accept,
362         .fn_recv                = ipv6_tcp_recv,
363         .fn_send                = ipv6_tcp_send,
364         .fn_close               = ipv6_tcp_close,
365
366         .fn_set_option          = ipv6_tcp_set_option,
367
368         .fn_get_peer_name       = ipv6_tcp_get_peer_name,
369         .fn_get_peer_addr       = ipv6_tcp_get_peer_addr,
370         .fn_get_peer_port       = ipv6_tcp_get_peer_port,
371         .fn_get_my_addr         = ipv6_tcp_get_my_addr,
372         .fn_get_my_port         = ipv6_tcp_get_my_port,
373
374         .fn_get_fd              = ipv6_tcp_get_fd
375 };
376
377 const struct socket_ops *socket_ipv6_ops(enum socket_type type)
378 {
379         if (type != SOCKET_TYPE_STREAM) {
380                 return NULL;
381         }
382         return &ipv6_tcp_ops;
383 }