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