r4831: added udp support to our generic sockets library.
[bbaumbach/samba-autobuild/.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
27 static NTSTATUS ipv4_init(struct socket_context *sock)
28 {
29         int type;
30
31         switch (sock->type) {
32         case SOCKET_TYPE_STREAM:
33                 type = SOCK_STREAM;
34                 break;
35         case SOCKET_TYPE_DGRAM:
36                 type = SOCK_DGRAM;
37                 break;
38         default:
39                 return NT_STATUS_INVALID_PARAMETER;
40         }
41
42         sock->fd = socket(PF_INET, type, 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 ipv4_close(struct socket_context *sock)
51 {
52         close(sock->fd);
53 }
54
55 static NTSTATUS ipv4_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
83 static NTSTATUS ipv4_connect(struct socket_context *sock,
84                                  const char *my_address, int my_port,
85                                  const char *srv_address, int srv_port,
86                                  uint32_t flags)
87 {
88         struct sockaddr_in srv_addr;
89         struct ipv4_addr my_ip;
90         struct ipv4_addr srv_ip;
91         int ret;
92
93         my_ip = interpret_addr2(my_address);
94
95         if (my_ip.addr != 0 || my_port != 0) {
96                 struct sockaddr_in my_addr;
97                 ZERO_STRUCT(my_addr);
98 #ifdef HAVE_SOCK_SIN_LEN
99                 my_addr.sin_len         = sizeof(my_addr);
100 #endif
101                 my_addr.sin_addr.s_addr = my_ip.addr;
102                 my_addr.sin_port        = htons(my_port);
103                 my_addr.sin_family      = PF_INET;
104                 
105                 ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
106                 if (ret == -1) {
107                         return map_nt_error_from_unix(errno);
108                 }
109         }
110
111         srv_ip = interpret_addr2(srv_address);
112
113         ZERO_STRUCT(srv_addr);
114 #ifdef HAVE_SOCK_SIN_LEN
115         srv_addr.sin_len        = sizeof(srv_addr);
116 #endif
117         srv_addr.sin_addr.s_addr= srv_ip.addr;
118         srv_addr.sin_port       = htons(srv_port);
119         srv_addr.sin_family     = PF_INET;
120
121         ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
122         if (ret == -1) {
123                 return map_nt_error_from_unix(errno);
124         }
125
126         return ipv4_connect_complete(sock, flags);
127 }
128
129
130 /*
131   note that for simplicity of the API, socket_listen() is also
132   use for DGRAM sockets, but in reality only a bind() is done
133 */
134 static NTSTATUS ipv4_listen(struct socket_context *sock,
135                             const char *my_address, int port,
136                             int queue_size, uint32_t flags)
137 {
138         struct sockaddr_in my_addr;
139         struct ipv4_addr ip_addr;
140         int ret;
141
142         ip_addr = interpret_addr2(my_address);
143
144         ZERO_STRUCT(my_addr);
145 #ifdef HAVE_SOCK_SIN_LEN
146         my_addr.sin_len         = sizeof(my_addr);
147 #endif
148         my_addr.sin_addr.s_addr = ip_addr.addr;
149         my_addr.sin_port        = htons(port);
150         my_addr.sin_family      = PF_INET;
151
152         ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
153         if (ret == -1) {
154                 return map_nt_error_from_unix(errno);
155         }
156
157         if (sock->type == SOCKET_TYPE_STREAM) {
158                 ret = listen(sock->fd, queue_size);
159                 if (ret == -1) {
160                         return map_nt_error_from_unix(errno);
161                 }
162         }
163
164         if (!(flags & SOCKET_FLAG_BLOCK)) {
165                 ret = set_blocking(sock->fd, False);
166                 if (ret == -1) {
167                         return map_nt_error_from_unix(errno);
168                 }
169         }
170
171         sock->state= SOCKET_STATE_SERVER_LISTEN;
172
173         return NT_STATUS_OK;
174 }
175
176 static NTSTATUS ipv4_accept(struct socket_context *sock, struct socket_context **new_sock)
177 {
178         struct sockaddr_in cli_addr;
179         socklen_t cli_addr_len = sizeof(cli_addr);
180         int new_fd;
181
182         if (sock->type != SOCKET_TYPE_STREAM) {
183                 return NT_STATUS_INVALID_PARAMETER;
184         }
185
186         new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
187         if (new_fd == -1) {
188                 return map_nt_error_from_unix(errno);
189         }
190
191         if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
192                 int ret = set_blocking(new_fd, False);
193                 if (ret == -1) {
194                         close(new_fd);
195                         return map_nt_error_from_unix(errno);
196                 }
197         }
198
199         /* TODO: we could add a 'accept_check' hook here
200          *       which get the black/white lists via socket_set_accept_filter()
201          *       or something like that
202          *       --metze
203          */
204
205         (*new_sock) = talloc_p(NULL, struct socket_context);
206         if (!(*new_sock)) {
207                 close(new_fd);
208                 return NT_STATUS_NO_MEMORY;
209         }
210
211         /* copy the socket_context */
212         (*new_sock)->type               = sock->type;
213         (*new_sock)->state              = SOCKET_STATE_SERVER_CONNECTED;
214         (*new_sock)->flags              = sock->flags;
215
216         (*new_sock)->fd                 = new_fd;
217
218         (*new_sock)->private_data       = NULL;
219         (*new_sock)->ops                = sock->ops;
220
221         return NT_STATUS_OK;
222 }
223
224 static NTSTATUS ipv4_recv(struct socket_context *sock, void *buf, 
225                               size_t wantlen, size_t *nread, uint32_t flags)
226 {
227         ssize_t gotlen;
228         int flgs = 0;
229
230         /* TODO: we need to map all flags here */
231         if (flags & SOCKET_FLAG_PEEK) {
232                 flgs |= MSG_PEEK;
233         }
234
235         if (flags & SOCKET_FLAG_BLOCK) {
236                 flgs |= MSG_WAITALL;
237         }
238
239         *nread = 0;
240
241         gotlen = recv(sock->fd, buf, wantlen, flgs);
242         if (gotlen == 0) {
243                 return NT_STATUS_END_OF_FILE;
244         } else if (gotlen == -1) {
245                 return map_nt_error_from_unix(errno);
246         }
247
248         *nread = gotlen;
249
250         return NT_STATUS_OK;
251 }
252
253
254 static NTSTATUS ipv4_recvfrom(struct socket_context *sock, void *buf, 
255                               size_t wantlen, size_t *nread, uint32_t flags,
256                               const char **src_addr, int *src_port)
257 {
258         ssize_t gotlen;
259         int flgs = 0;
260         struct sockaddr_in from_addr;
261         socklen_t from_len = sizeof(from_addr);
262         const char *addr;
263
264         if (flags & SOCKET_FLAG_PEEK) {
265                 flgs |= MSG_PEEK;
266         }
267
268         if (flags & SOCKET_FLAG_BLOCK) {
269                 flgs |= MSG_WAITALL;
270         }
271
272         *nread = 0;
273
274         gotlen = recvfrom(sock->fd, buf, wantlen, flgs, 
275                           (struct sockaddr *)&from_addr, &from_len);
276         if (gotlen == 0) {
277                 return NT_STATUS_END_OF_FILE;
278         } else if (gotlen == -1) {
279                 return map_nt_error_from_unix(errno);
280         }
281
282         addr = inet_ntoa(from_addr.sin_addr);
283         if (addr == NULL) {
284                 return NT_STATUS_INTERNAL_ERROR;
285         }
286         *src_addr = talloc_strdup(sock, addr);
287         NT_STATUS_HAVE_NO_MEMORY(*src_addr);
288         *src_port = ntohs(from_addr.sin_port);
289
290         *nread = gotlen;
291
292         return NT_STATUS_OK;
293 }
294
295 static NTSTATUS ipv4_send(struct socket_context *sock, 
296                               const DATA_BLOB *blob, size_t *sendlen, uint32_t flags)
297 {
298         ssize_t len;
299         int flgs = 0;
300
301         *sendlen = 0;
302
303         len = send(sock->fd, blob->data, blob->length, flgs);
304         if (len == -1) {
305                 return map_nt_error_from_unix(errno);
306         }       
307
308         *sendlen = len;
309
310         return NT_STATUS_OK;
311 }
312
313 static NTSTATUS ipv4_sendto(struct socket_context *sock, 
314                             const DATA_BLOB *blob, size_t *sendlen, uint32_t flags,
315                             const char *dest_addr, int dest_port)
316 {
317         ssize_t len;
318         int flgs = 0;
319         struct sockaddr_in srv_addr;
320         struct ipv4_addr addr;
321
322         ZERO_STRUCT(srv_addr);
323 #ifdef HAVE_SOCK_SIN_LEN
324         srv_addr.sin_len         = sizeof(srv_addr);
325 #endif
326         addr                     = interpret_addr2(dest_addr);
327         srv_addr.sin_addr.s_addr = addr.addr;
328         srv_addr.sin_port        = htons(dest_port);
329         srv_addr.sin_family      = PF_INET;
330
331         *sendlen = 0;
332
333         len = sendto(sock->fd, blob->data, blob->length, flgs, 
334                    (struct sockaddr *)&srv_addr, sizeof(srv_addr));
335         if (len == -1) {
336                 return map_nt_error_from_unix(errno);
337         }       
338
339         *sendlen = len;
340
341         return NT_STATUS_OK;
342 }
343
344 static NTSTATUS ipv4_set_option(struct socket_context *sock, const char *option, const char *val)
345 {
346         set_socket_options(sock->fd, option);
347         return NT_STATUS_OK;
348 }
349
350 static char *ipv4_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
351 {
352         struct sockaddr_in peer_addr;
353         socklen_t len = sizeof(peer_addr);
354         struct hostent *he;
355         int ret;
356
357         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
358         if (ret == -1) {
359                 return NULL;
360         }
361
362         he = gethostbyaddr((char *)&peer_addr.sin_addr, sizeof(peer_addr.sin_addr), AF_INET);
363         if (he == NULL) {
364                 return NULL;
365         }
366
367         return talloc_strdup(mem_ctx, he->h_name);
368 }
369
370 static char *ipv4_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
371 {
372         struct sockaddr_in peer_addr;
373         socklen_t len = sizeof(peer_addr);
374         int ret;
375
376         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
377         if (ret == -1) {
378                 return NULL;
379         }
380
381         return talloc_strdup(mem_ctx, inet_ntoa(peer_addr.sin_addr));
382 }
383
384 static int ipv4_get_peer_port(struct socket_context *sock)
385 {
386         struct sockaddr_in peer_addr;
387         socklen_t len = sizeof(peer_addr);
388         int ret;
389
390         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
391         if (ret == -1) {
392                 return -1;
393         }
394
395         return ntohs(peer_addr.sin_port);
396 }
397
398 static char *ipv4_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
399 {
400         struct sockaddr_in my_addr;
401         socklen_t len = sizeof(my_addr);
402         int ret;
403
404         ret = getsockname(sock->fd, (struct sockaddr *)&my_addr, &len);
405         if (ret == -1) {
406                 return NULL;
407         }
408
409         return talloc_strdup(mem_ctx, inet_ntoa(my_addr.sin_addr));
410 }
411
412 static int ipv4_get_my_port(struct socket_context *sock)
413 {
414         struct sockaddr_in my_addr;
415         socklen_t len = sizeof(my_addr);
416         int ret;
417
418         ret = getsockname(sock->fd, (struct sockaddr *)&my_addr, &len);
419         if (ret == -1) {
420                 return -1;
421         }
422
423         return ntohs(my_addr.sin_port);
424 }
425
426 static int ipv4_get_fd(struct socket_context *sock)
427 {
428         return sock->fd;
429 }
430
431 static const struct socket_ops ipv4_ops = {
432         .name                   = "ipv4",
433         .fn_init                = ipv4_init,
434         .fn_connect             = ipv4_connect,
435         .fn_connect_complete    = ipv4_connect_complete,
436         .fn_listen              = ipv4_listen,
437         .fn_accept              = ipv4_accept,
438         .fn_recv                = ipv4_recv,
439         .fn_recvfrom            = ipv4_recvfrom,
440         .fn_send                = ipv4_send,
441         .fn_sendto              = ipv4_sendto,
442         .fn_close               = ipv4_close,
443
444         .fn_set_option          = ipv4_set_option,
445
446         .fn_get_peer_name       = ipv4_get_peer_name,
447         .fn_get_peer_addr       = ipv4_get_peer_addr,
448         .fn_get_peer_port       = ipv4_get_peer_port,
449         .fn_get_my_addr         = ipv4_get_my_addr,
450         .fn_get_my_port         = ipv4_get_my_port,
451
452         .fn_get_fd              = ipv4_get_fd
453 };
454
455 const struct socket_ops *socket_ipv4_ops(enum socket_type type)
456 {
457         return &ipv4_ops;
458 }