r9705: r9685@blu: tridge | 2005-08-27 19:43:44 +1000
[bbaumbach/samba-autobuild/.git] / source / 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         (*new_sock)->backend_name       = sock->backend_name;
230
231         return NT_STATUS_OK;
232 }
233
234 static NTSTATUS ipv4_recv(struct socket_context *sock, void *buf, 
235                               size_t wantlen, size_t *nread, uint32_t flags)
236 {
237         ssize_t gotlen;
238         int flgs = 0;
239
240         /* TODO: we need to map all flags here */
241         if (flags & SOCKET_FLAG_PEEK) {
242                 flgs |= MSG_PEEK;
243         }
244
245         if (flags & SOCKET_FLAG_BLOCK) {
246                 flgs |= MSG_WAITALL;
247         }
248
249         *nread = 0;
250
251         gotlen = recv(sock->fd, buf, wantlen, flgs);
252         if (gotlen == 0) {
253                 return NT_STATUS_END_OF_FILE;
254         } else if (gotlen == -1) {
255                 return map_nt_error_from_unix(errno);
256         }
257
258         *nread = gotlen;
259
260         return NT_STATUS_OK;
261 }
262
263
264 static NTSTATUS ipv4_recvfrom(struct socket_context *sock, void *buf, 
265                               size_t wantlen, size_t *nread, uint32_t flags,
266                               const char **src_addr, int *src_port)
267 {
268         ssize_t gotlen;
269         int flgs = 0;
270         struct sockaddr_in from_addr;
271         socklen_t from_len = sizeof(from_addr);
272         const char *addr;
273
274         if (flags & SOCKET_FLAG_PEEK) {
275                 flgs |= MSG_PEEK;
276         }
277
278         if (flags & SOCKET_FLAG_BLOCK) {
279                 flgs |= MSG_WAITALL;
280         }
281
282         *nread = 0;
283
284         gotlen = recvfrom(sock->fd, buf, wantlen, flgs, 
285                           (struct sockaddr *)&from_addr, &from_len);
286         if (gotlen == 0) {
287                 return NT_STATUS_END_OF_FILE;
288         } else if (gotlen == -1) {
289                 return map_nt_error_from_unix(errno);
290         }
291
292         addr = inet_ntoa(from_addr.sin_addr);
293         if (addr == NULL) {
294                 return NT_STATUS_INTERNAL_ERROR;
295         }
296         *src_addr = talloc_strdup(sock, addr);
297         NT_STATUS_HAVE_NO_MEMORY(*src_addr);
298         *src_port = ntohs(from_addr.sin_port);
299
300         *nread = gotlen;
301
302         return NT_STATUS_OK;
303 }
304
305 static NTSTATUS ipv4_send(struct socket_context *sock, 
306                               const DATA_BLOB *blob, size_t *sendlen, uint32_t flags)
307 {
308         ssize_t len;
309         int flgs = 0;
310
311         *sendlen = 0;
312
313         len = send(sock->fd, blob->data, blob->length, flgs);
314         if (len == -1) {
315                 return map_nt_error_from_unix(errno);
316         }       
317
318         *sendlen = len;
319
320         return NT_STATUS_OK;
321 }
322
323 static NTSTATUS ipv4_sendto(struct socket_context *sock, 
324                             const DATA_BLOB *blob, size_t *sendlen, uint32_t flags,
325                             const char *dest_addr, int dest_port)
326 {
327         ssize_t len;
328         int flgs = 0;
329         struct sockaddr_in srv_addr;
330         struct ipv4_addr addr;
331
332         ZERO_STRUCT(srv_addr);
333 #ifdef HAVE_SOCK_SIN_LEN
334         srv_addr.sin_len         = sizeof(srv_addr);
335 #endif
336         addr                     = interpret_addr2(dest_addr);
337         srv_addr.sin_addr.s_addr = addr.addr;
338         srv_addr.sin_port        = htons(dest_port);
339         srv_addr.sin_family      = PF_INET;
340
341         *sendlen = 0;
342
343         len = sendto(sock->fd, blob->data, blob->length, flgs, 
344                    (struct sockaddr *)&srv_addr, sizeof(srv_addr));
345         if (len == -1) {
346                 return map_nt_error_from_unix(errno);
347         }       
348
349         *sendlen = len;
350
351         return NT_STATUS_OK;
352 }
353
354 static NTSTATUS ipv4_set_option(struct socket_context *sock, const char *option, const char *val)
355 {
356         set_socket_options(sock->fd, option);
357         return NT_STATUS_OK;
358 }
359
360 static char *ipv4_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
361 {
362         struct sockaddr_in peer_addr;
363         socklen_t len = sizeof(peer_addr);
364         struct hostent *he;
365         int ret;
366
367         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
368         if (ret == -1) {
369                 return NULL;
370         }
371
372         he = gethostbyaddr((char *)&peer_addr.sin_addr, sizeof(peer_addr.sin_addr), AF_INET);
373         if (he == NULL) {
374                 return NULL;
375         }
376
377         return talloc_strdup(mem_ctx, he->h_name);
378 }
379
380 static char *ipv4_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
381 {
382         struct sockaddr_in peer_addr;
383         socklen_t len = sizeof(peer_addr);
384         int ret;
385
386         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
387         if (ret == -1) {
388                 return NULL;
389         }
390
391         return talloc_strdup(mem_ctx, inet_ntoa(peer_addr.sin_addr));
392 }
393
394 static int ipv4_get_peer_port(struct socket_context *sock)
395 {
396         struct sockaddr_in peer_addr;
397         socklen_t len = sizeof(peer_addr);
398         int ret;
399
400         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
401         if (ret == -1) {
402                 return -1;
403         }
404
405         return ntohs(peer_addr.sin_port);
406 }
407
408 static char *ipv4_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
409 {
410         struct sockaddr_in my_addr;
411         socklen_t len = sizeof(my_addr);
412         int ret;
413
414         ret = getsockname(sock->fd, (struct sockaddr *)&my_addr, &len);
415         if (ret == -1) {
416                 return NULL;
417         }
418
419         return talloc_strdup(mem_ctx, inet_ntoa(my_addr.sin_addr));
420 }
421
422 static int ipv4_get_my_port(struct socket_context *sock)
423 {
424         struct sockaddr_in my_addr;
425         socklen_t len = sizeof(my_addr);
426         int ret;
427
428         ret = getsockname(sock->fd, (struct sockaddr *)&my_addr, &len);
429         if (ret == -1) {
430                 return -1;
431         }
432
433         return ntohs(my_addr.sin_port);
434 }
435
436 static int ipv4_get_fd(struct socket_context *sock)
437 {
438         return sock->fd;
439 }
440
441 static NTSTATUS ipv4_pending(struct socket_context *sock, size_t *npending)
442 {
443         int value = 0;
444         if (ioctl(sock->fd, FIONREAD, &value) == 0) {
445                 *npending = value;
446                 return NT_STATUS_OK;
447         }
448         return map_nt_error_from_unix(errno);
449 }
450
451 static const struct socket_ops ipv4_ops = {
452         .name                   = "ipv4",
453         .fn_init                = ipv4_init,
454         .fn_connect             = ipv4_connect,
455         .fn_connect_complete    = ipv4_connect_complete,
456         .fn_listen              = ipv4_listen,
457         .fn_accept              = ipv4_accept,
458         .fn_recv                = ipv4_recv,
459         .fn_recvfrom            = ipv4_recvfrom,
460         .fn_send                = ipv4_send,
461         .fn_sendto              = ipv4_sendto,
462         .fn_pending             = ipv4_pending,
463         .fn_close               = ipv4_close,
464
465         .fn_set_option          = ipv4_set_option,
466
467         .fn_get_peer_name       = ipv4_get_peer_name,
468         .fn_get_peer_addr       = ipv4_get_peer_addr,
469         .fn_get_peer_port       = ipv4_get_peer_port,
470         .fn_get_my_addr         = ipv4_get_my_addr,
471         .fn_get_my_port         = ipv4_get_my_port,
472
473         .fn_get_fd              = ipv4_get_fd
474 };
475
476 const struct socket_ops *socket_ipv4_ops(enum socket_type type)
477 {
478         return &ipv4_ops;
479 }