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