9c393a77ec31c35f3fe091ecb019318bc0306171
[ira/wip.git] / source4 / lib / socket / socket_ipv4.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Socket IPv4 functions
5
6    Copyright (C) Stefan Metzmacher 2004
7    Copyright (C) Andrew Tridgell 2004-2005
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "system/network.h"
26 #include "system/filesys.h"
27 #include "lib/socket/socket.h"
28
29 static NTSTATUS ipv4_init(struct socket_context *sock)
30 {
31         int type;
32
33         switch (sock->type) {
34         case SOCKET_TYPE_STREAM:
35                 type = SOCK_STREAM;
36                 break;
37         case SOCKET_TYPE_DGRAM:
38                 type = SOCK_DGRAM;
39                 break;
40         default:
41                 return NT_STATUS_INVALID_PARAMETER;
42         }
43
44         sock->fd = socket(PF_INET, type, 0);
45         if (sock->fd == -1) {
46                 return map_nt_error_from_unix(errno);
47         }
48
49         sock->backend_name = "ipv4";
50
51         return NT_STATUS_OK;
52 }
53
54 static void ipv4_close(struct socket_context *sock)
55 {
56         close(sock->fd);
57 }
58
59 static NTSTATUS ipv4_connect_complete(struct socket_context *sock, uint32_t flags)
60 {
61         int error=0, ret;
62         socklen_t len = sizeof(error);
63
64         /* check for any errors that may have occurred - this is needed
65            for non-blocking connect */
66         ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
67         if (ret == -1) {
68                 return map_nt_error_from_unix(errno);
69         }
70         if (error != 0) {
71                 return map_nt_error_from_unix(error);
72         }
73
74         if (!(flags & SOCKET_FLAG_BLOCK)) {
75                 ret = set_blocking(sock->fd, False);
76                 if (ret == -1) {
77                         return map_nt_error_from_unix(errno);
78                 }
79         }
80
81         sock->state = SOCKET_STATE_CLIENT_CONNECTED;
82
83         return NT_STATUS_OK;
84 }
85
86
87 static NTSTATUS ipv4_connect(struct socket_context *sock,
88                                  const char *my_address, int my_port,
89                                  const char *srv_address, int srv_port,
90                                  uint32_t flags)
91 {
92         struct sockaddr_in srv_addr;
93         struct ipv4_addr my_ip;
94         struct ipv4_addr srv_ip;
95         int ret;
96
97         my_ip = interpret_addr2(my_address);
98
99         if (my_ip.addr != 0 || my_port != 0) {
100                 struct sockaddr_in my_addr;
101                 ZERO_STRUCT(my_addr);
102 #ifdef HAVE_SOCK_SIN_LEN
103                 my_addr.sin_len         = sizeof(my_addr);
104 #endif
105                 my_addr.sin_addr.s_addr = my_ip.addr;
106                 my_addr.sin_port        = htons(my_port);
107                 my_addr.sin_family      = PF_INET;
108                 
109                 ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
110                 if (ret == -1) {
111                         return map_nt_error_from_unix(errno);
112                 }
113         }
114
115         srv_ip = interpret_addr2(srv_address);
116         if (!srv_ip.addr) {
117                 return NT_STATUS_BAD_NETWORK_NAME;
118         }
119
120         ZERO_STRUCT(srv_addr);
121 #ifdef HAVE_SOCK_SIN_LEN
122         srv_addr.sin_len        = sizeof(srv_addr);
123 #endif
124         srv_addr.sin_addr.s_addr= srv_ip.addr;
125         srv_addr.sin_port       = htons(srv_port);
126         srv_addr.sin_family     = PF_INET;
127
128         ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
129         if (ret == -1) {
130                 return map_nt_error_from_unix(errno);
131         }
132
133         return ipv4_connect_complete(sock, flags);
134 }
135
136
137 /*
138   note that for simplicity of the API, socket_listen() is also
139   use for DGRAM sockets, but in reality only a bind() is done
140 */
141 static NTSTATUS ipv4_listen(struct socket_context *sock,
142                             const char *my_address, int port,
143                             int queue_size, uint32_t flags)
144 {
145         struct sockaddr_in my_addr;
146         struct ipv4_addr ip_addr;
147         int ret;
148
149         socket_set_option(sock, "SO_REUSEADDR=1", NULL);
150
151         ip_addr = interpret_addr2(my_address);
152
153         ZERO_STRUCT(my_addr);
154 #ifdef HAVE_SOCK_SIN_LEN
155         my_addr.sin_len         = sizeof(my_addr);
156 #endif
157         my_addr.sin_addr.s_addr = ip_addr.addr;
158         my_addr.sin_port        = htons(port);
159         my_addr.sin_family      = PF_INET;
160
161         ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
162         if (ret == -1) {
163                 return map_nt_error_from_unix(errno);
164         }
165
166         if (sock->type == SOCKET_TYPE_STREAM) {
167                 ret = listen(sock->fd, queue_size);
168                 if (ret == -1) {
169                         return map_nt_error_from_unix(errno);
170                 }
171         }
172
173         if (!(flags & SOCKET_FLAG_BLOCK)) {
174                 ret = set_blocking(sock->fd, False);
175                 if (ret == -1) {
176                         return map_nt_error_from_unix(errno);
177                 }
178         }
179
180         sock->state= SOCKET_STATE_SERVER_LISTEN;
181
182         return NT_STATUS_OK;
183 }
184
185 static NTSTATUS ipv4_accept(struct socket_context *sock, struct socket_context **new_sock)
186 {
187         struct sockaddr_in cli_addr;
188         socklen_t cli_addr_len = sizeof(cli_addr);
189         int new_fd;
190
191         if (sock->type != SOCKET_TYPE_STREAM) {
192                 return NT_STATUS_INVALID_PARAMETER;
193         }
194
195         new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
196         if (new_fd == -1) {
197                 return map_nt_error_from_unix(errno);
198         }
199
200         if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
201                 int ret = set_blocking(new_fd, False);
202                 if (ret == -1) {
203                         close(new_fd);
204                         return map_nt_error_from_unix(errno);
205                 }
206         }
207
208         /* TODO: we could add a 'accept_check' hook here
209          *       which get the black/white lists via socket_set_accept_filter()
210          *       or something like that
211          *       --metze
212          */
213
214         (*new_sock) = talloc(NULL, struct socket_context);
215         if (!(*new_sock)) {
216                 close(new_fd);
217                 return NT_STATUS_NO_MEMORY;
218         }
219
220         /* copy the socket_context */
221         (*new_sock)->type               = sock->type;
222         (*new_sock)->state              = SOCKET_STATE_SERVER_CONNECTED;
223         (*new_sock)->flags              = sock->flags;
224
225         (*new_sock)->fd                 = new_fd;
226
227         (*new_sock)->private_data       = NULL;
228         (*new_sock)->ops                = sock->ops;
229
230         return NT_STATUS_OK;
231 }
232
233 static NTSTATUS ipv4_recv(struct socket_context *sock, void *buf, 
234                               size_t wantlen, size_t *nread, uint32_t flags)
235 {
236         ssize_t gotlen;
237         int flgs = 0;
238
239         /* TODO: we need to map all flags here */
240         if (flags & SOCKET_FLAG_PEEK) {
241                 flgs |= MSG_PEEK;
242         }
243
244         if (flags & SOCKET_FLAG_BLOCK) {
245                 flgs |= MSG_WAITALL;
246         }
247
248         *nread = 0;
249
250         gotlen = recv(sock->fd, buf, wantlen, flgs);
251         if (gotlen == 0) {
252                 return NT_STATUS_END_OF_FILE;
253         } else if (gotlen == -1) {
254                 return map_nt_error_from_unix(errno);
255         }
256
257         *nread = gotlen;
258
259         return NT_STATUS_OK;
260 }
261
262
263 static NTSTATUS ipv4_recvfrom(struct socket_context *sock, void *buf, 
264                               size_t wantlen, size_t *nread, uint32_t flags,
265                               const char **src_addr, int *src_port)
266 {
267         ssize_t gotlen;
268         int flgs = 0;
269         struct sockaddr_in from_addr;
270         socklen_t from_len = sizeof(from_addr);
271         const char *addr;
272
273         if (flags & SOCKET_FLAG_PEEK) {
274                 flgs |= MSG_PEEK;
275         }
276
277         if (flags & SOCKET_FLAG_BLOCK) {
278                 flgs |= MSG_WAITALL;
279         }
280
281         *nread = 0;
282
283         gotlen = recvfrom(sock->fd, buf, wantlen, flgs, 
284                           (struct sockaddr *)&from_addr, &from_len);
285         if (gotlen == 0) {
286                 return NT_STATUS_END_OF_FILE;
287         } else if (gotlen == -1) {
288                 return map_nt_error_from_unix(errno);
289         }
290
291         addr = inet_ntoa(from_addr.sin_addr);
292         if (addr == NULL) {
293                 return NT_STATUS_INTERNAL_ERROR;
294         }
295         *src_addr = talloc_strdup(sock, addr);
296         NT_STATUS_HAVE_NO_MEMORY(*src_addr);
297         *src_port = ntohs(from_addr.sin_port);
298
299         *nread = gotlen;
300
301         return NT_STATUS_OK;
302 }
303
304 static NTSTATUS ipv4_send(struct socket_context *sock, 
305                               const DATA_BLOB *blob, size_t *sendlen, uint32_t flags)
306 {
307         ssize_t len;
308         int flgs = 0;
309
310         *sendlen = 0;
311
312         len = send(sock->fd, blob->data, blob->length, flgs);
313         if (len == -1) {
314                 return map_nt_error_from_unix(errno);
315         }       
316
317         *sendlen = len;
318
319         return NT_STATUS_OK;
320 }
321
322 static NTSTATUS ipv4_sendto(struct socket_context *sock, 
323                             const DATA_BLOB *blob, size_t *sendlen, uint32_t flags,
324                             const char *dest_addr, int dest_port)
325 {
326         ssize_t len;
327         int flgs = 0;
328         struct sockaddr_in srv_addr;
329         struct ipv4_addr addr;
330
331         ZERO_STRUCT(srv_addr);
332 #ifdef HAVE_SOCK_SIN_LEN
333         srv_addr.sin_len         = sizeof(srv_addr);
334 #endif
335         addr                     = interpret_addr2(dest_addr);
336         srv_addr.sin_addr.s_addr = addr.addr;
337         srv_addr.sin_port        = htons(dest_port);
338         srv_addr.sin_family      = PF_INET;
339
340         *sendlen = 0;
341
342         len = sendto(sock->fd, blob->data, blob->length, flgs, 
343                    (struct sockaddr *)&srv_addr, sizeof(srv_addr));
344         if (len == -1) {
345                 return map_nt_error_from_unix(errno);
346         }       
347
348         *sendlen = len;
349
350         return NT_STATUS_OK;
351 }
352
353 static NTSTATUS ipv4_set_option(struct socket_context *sock, const char *option, const char *val)
354 {
355         set_socket_options(sock->fd, option);
356         return NT_STATUS_OK;
357 }
358
359 static char *ipv4_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
360 {
361         struct sockaddr_in peer_addr;
362         socklen_t len = sizeof(peer_addr);
363         struct hostent *he;
364         int ret;
365
366         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
367         if (ret == -1) {
368                 return NULL;
369         }
370
371         he = gethostbyaddr((char *)&peer_addr.sin_addr, sizeof(peer_addr.sin_addr), AF_INET);
372         if (he == NULL) {
373                 return NULL;
374         }
375
376         return talloc_strdup(mem_ctx, he->h_name);
377 }
378
379 static char *ipv4_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
380 {
381         struct sockaddr_in peer_addr;
382         socklen_t len = sizeof(peer_addr);
383         int ret;
384
385         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
386         if (ret == -1) {
387                 return NULL;
388         }
389
390         return talloc_strdup(mem_ctx, inet_ntoa(peer_addr.sin_addr));
391 }
392
393 static int ipv4_get_peer_port(struct socket_context *sock)
394 {
395         struct sockaddr_in peer_addr;
396         socklen_t len = sizeof(peer_addr);
397         int ret;
398
399         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
400         if (ret == -1) {
401                 return -1;
402         }
403
404         return ntohs(peer_addr.sin_port);
405 }
406
407 static char *ipv4_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
408 {
409         struct sockaddr_in my_addr;
410         socklen_t len = sizeof(my_addr);
411         int ret;
412
413         ret = getsockname(sock->fd, (struct sockaddr *)&my_addr, &len);
414         if (ret == -1) {
415                 return NULL;
416         }
417
418         return talloc_strdup(mem_ctx, inet_ntoa(my_addr.sin_addr));
419 }
420
421 static int ipv4_get_my_port(struct socket_context *sock)
422 {
423         struct sockaddr_in my_addr;
424         socklen_t len = sizeof(my_addr);
425         int ret;
426
427         ret = getsockname(sock->fd, (struct sockaddr *)&my_addr, &len);
428         if (ret == -1) {
429                 return -1;
430         }
431
432         return ntohs(my_addr.sin_port);
433 }
434
435 static int ipv4_get_fd(struct socket_context *sock)
436 {
437         return sock->fd;
438 }
439
440 static NTSTATUS ipv4_pending(struct socket_context *sock, size_t *npending)
441 {
442         int value = 0;
443         if (ioctl(sock->fd, FIONREAD, &value) == 0) {
444                 *npending = value;
445                 return NT_STATUS_OK;
446         }
447         return map_nt_error_from_unix(errno);
448 }
449
450 static const struct socket_ops ipv4_ops = {
451         .name                   = "ipv4",
452         .fn_init                = ipv4_init,
453         .fn_connect             = ipv4_connect,
454         .fn_connect_complete    = ipv4_connect_complete,
455         .fn_listen              = ipv4_listen,
456         .fn_accept              = ipv4_accept,
457         .fn_recv                = ipv4_recv,
458         .fn_recvfrom            = ipv4_recvfrom,
459         .fn_send                = ipv4_send,
460         .fn_sendto              = ipv4_sendto,
461         .fn_pending             = ipv4_pending,
462         .fn_close               = ipv4_close,
463
464         .fn_set_option          = ipv4_set_option,
465
466         .fn_get_peer_name       = ipv4_get_peer_name,
467         .fn_get_peer_addr       = ipv4_get_peer_addr,
468         .fn_get_peer_port       = ipv4_get_peer_port,
469         .fn_get_my_addr         = ipv4_get_my_addr,
470         .fn_get_my_port         = ipv4_get_my_port,
471
472         .fn_get_fd              = ipv4_get_fd
473 };
474
475 const struct socket_ops *socket_ipv4_ops(enum socket_type type)
476 {
477         return &ipv4_ops;
478 }