23e34dd39b3c24d5d3c5fc713aba69b7bbb18e9d
[bbaumbach/samba-autobuild/.git] / source4 / lib / socket / socket_ipv4.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Socket IPv4 functions
4    Copyright (C) Stefan Metzmacher 2004
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 static NTSTATUS ipv4_tcp_init(struct socket_context *sock)
24 {
25         sock->fd = socket(PF_INET, SOCK_STREAM, 0);
26         if (sock->fd == -1) {
27                 /* TODO: we need to map from errno to NTSTATUS here! */ 
28                 return NT_STATUS_FOOBAR;
29         }
30
31         return NT_STATUS_OK;
32 }
33
34 static void ipv4_tcp_close(struct socket_context *sock)
35 {
36         close(sock->fd);
37 }
38
39 static NTSTATUS ipv4_tcp_connect(struct socket_context *sock,
40                                         const char *my_address, int my_port,
41                                         const char *srv_address, int srv_port,
42                                         uint32_t flags)
43 {
44         struct sockaddr_in my_addr;
45         struct sockaddr_in srv_addr;
46         struct in_addr my_ip;
47         struct in_addr srv_ip;
48         int ret;
49
50         ret = inet_aton(my_address, &my_ip);
51         if (ret == 0) {
52                 /* not a valid ipv4 address */
53                 return NT_STATUS_FOOBAR;
54         }
55
56         ZERO_STRUCT(my_addr);
57 #ifdef HAVE_SOCK_SIN_LEN
58         my_addr.sin_len         = sizeof(my_addr);
59 #endif
60         my_addr.sin_addr        = my_ip;
61         my_addr.sin_port        = htons(my_port);
62         my_addr.sin_family      = PF_INET;
63
64         ret = inet_aton(srv_address, &srv_ip);
65         if (ret == 0) {
66                 /* not a valid ipv4 address */
67                 return NT_STATUS_FOOBAR;
68         }
69
70         ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
71         if (ret == -1) {
72                 /* TODO: we need to map from errno to NTSTATUS here! */
73                 return NT_STATUS_FOOBAR;
74         }
75
76         ZERO_STRUCT(srv_addr);
77 #ifdef HAVE_SOCK_SIN_LEN
78         srv_addr.sin_len        = sizeof(srv_addr);
79 #endif
80         srv_addr.sin_addr       = srv_ip;
81         srv_addr.sin_port       = htons(srv_port);
82         srv_addr.sin_family     = PF_INET;
83
84         if (!(flags & SOCKET_FLAG_BLOCK)) {
85                 ret = set_blocking(sock->fd, False);
86                 if (ret == -1) {
87                         /* TODO: we need to map from errno to NTSTATUS here! */
88                         return NT_STATUS_FOOBAR;
89                 }
90         }
91
92         ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
93         if (ret == -1) {
94                 /* TODO: we need to map from errno to NTSTATUS here! */
95                 return NT_STATUS_FOOBAR;
96         }
97
98         sock->state = SOCKET_STATE_CLIENT_CONNECTED;
99
100         return NT_STATUS_OK;
101 }
102
103 static NTSTATUS ipv4_tcp_listen(struct socket_context *sock,
104                                         const char *my_address, int port,
105                                         int queue_size, uint32_t flags)
106 {
107         struct sockaddr_in my_addr;
108         struct in_addr ip_addr;
109         int ret;
110
111         ret = inet_aton(my_address, &ip_addr);
112         if (ret == 0) {
113                 /* not a valid ipv4 address */
114                 return NT_STATUS_FOOBAR;
115         }
116
117         ZERO_STRUCT(my_addr);
118 #ifdef HAVE_SOCK_SIN_LEN
119         my_addr.sin_len         = sizeof(my_addr);
120 #endif
121         my_addr.sin_addr        = ip_addr;
122         my_addr.sin_port        = htons(port);
123         my_addr.sin_family      = PF_INET;
124
125         ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
126         if (ret == -1) {
127                 /* TODO: we need to map from errno to NTSTATUS here! */
128                 return NT_STATUS_FOOBAR;
129         }
130
131         ret = listen(sock->fd, queue_size);
132         if (ret == -1) {
133                 /* TODO: we need to map from errno to NTSTATUS here! */
134                 return NT_STATUS_FOOBAR;
135         }
136
137         if (!(flags & SOCKET_FLAG_BLOCK)) {
138                 ret = set_blocking(sock->fd, False);
139                 if (ret == -1) {
140                         /* TODO: we need to map from errno to NTSTATUS here! */
141                         return NT_STATUS_FOOBAR;
142                 }
143         }
144
145         sock->state= SOCKET_STATE_SERVER_LISTEN;
146
147         return NT_STATUS_OK;
148 }
149
150 static NTSTATUS ipv4_tcp_accept(struct socket_context *sock, struct socket_context **new_sock, uint32_t flags)
151 {
152         struct sockaddr_in cli_addr;
153         socklen_t cli_addr_len = sizeof(cli_addr);
154         int new_fd;
155
156         new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
157         if (new_fd == -1) {
158                 /* TODO: we need to map from errno to NTSTATUS here! */
159                 return NT_STATUS_FOOBAR;
160         }
161
162         /* TODO: we could add a 'accept_check' hook here
163          *       which get the black/white lists via socket_set_accept_filter()
164          *       or something like that
165          *       --metze
166          */
167
168         (*new_sock) = talloc_p(NULL, struct socket_context);
169         if (!(*new_sock)) {
170                 close(new_fd);
171                 return NT_STATUS_NO_MEMORY;
172         }
173
174         /* copy the socket_context */
175         (*new_sock)->type               = sock->type;
176         (*new_sock)->state              = SOCKET_STATE_SERVER_CONNECTED;
177         (*new_sock)->flags              = flags;
178
179         (*new_sock)->fd                 = new_fd;
180
181         (*new_sock)->private_data       = NULL;
182         (*new_sock)->ops                = sock->ops;
183
184         return NT_STATUS_OK;
185 }
186
187 static NTSTATUS ipv4_tcp_recv(struct socket_context *sock, TALLOC_CTX *mem_ctx,
188                               DATA_BLOB *blob, size_t wantlen, uint32_t flags)
189 {
190         ssize_t gotlen;
191         void *buf;
192         int flgs = 0;
193
194         buf = talloc(mem_ctx, wantlen);
195         if (!buf) {
196                 return NT_STATUS_NO_MEMORY;
197         }
198
199         /* TODO: we need to map all flags here */
200         if (flags & SOCKET_FLAG_PEEK) {
201                 flgs |= MSG_PEEK;
202         }
203
204         if (!(flags & SOCKET_FLAG_BLOCK)) {
205                 flgs |= MSG_DONTWAIT;
206         }
207
208         if (flags & SOCKET_FLAG_BLOCK) {
209                 flgs |= MSG_WAITALL;
210         }
211
212         gotlen = recv(sock->fd, buf, wantlen, flgs);
213         if (gotlen == 0) {
214                 talloc_free(buf);
215                 return NT_STATUS_END_OF_FILE;
216         } else if (gotlen == -1) {
217                 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
218                 switch (errno) {
219                         case EBADF:
220                         case ENOTCONN:
221                         case ENOTSOCK:
222                         case EFAULT:
223                         case EINVAL:
224                                 status = NT_STATUS_INVALID_PARAMETER;
225                                 break;
226                         case EAGAIN:
227                         case EINTR:
228                                 status = STATUS_MORE_ENTRIES;
229                                 break;
230                         case ECONNREFUSED:
231                                 status = NT_STATUS_CONNECTION_REFUSED;
232                                 break;
233                 }
234                 talloc_free(buf);
235                 return status;
236         }
237
238         blob->length = gotlen;
239         blob->data = talloc_realloc(mem_ctx, buf, gotlen);
240         if (!blob->data) {
241                 return NT_STATUS_NO_MEMORY;
242         }
243
244         return NT_STATUS_OK;
245 }
246
247 static NTSTATUS ipv4_tcp_send(struct socket_context *sock, TALLOC_CTX *mem_ctx,
248                                         const DATA_BLOB *blob, size_t *sendlen, uint32_t flags)
249 {
250         ssize_t len;
251         int flgs = 0;
252
253         /* TODO: we need to map all flags here */
254         if (!(flags & SOCKET_FLAG_BLOCK)) {
255                 flgs |= MSG_DONTWAIT;
256         }
257
258         len = send(sock->fd, blob->data, blob->length, flgs);
259         if (len == -1) {
260                 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
261                 switch (errno) {
262                         case EBADF:
263                         case ENOTSOCK:
264                         case EFAULT:
265                         case EINVAL:
266                                 status = NT_STATUS_INVALID_PARAMETER;
267                                 break;
268                         case EMSGSIZE:
269                                 status = NT_STATUS_INVALID_BUFFER_SIZE;
270                                 break;
271                         case EAGAIN:
272                         /*case EWOULDBLOCK: this is an alis of EAGAIN --metze */
273                         case EINTR:
274                                 *sendlen = 0;
275                                 status = STATUS_MORE_ENTRIES;
276                                 break;
277                         case ENOBUFS:
278                                 status = NT_STATUS_FOOBAR;
279                                 break;
280                         case ENOMEM:
281                                 status = NT_STATUS_NO_MEMORY;
282                                 break;
283                         case EPIPE:
284                                 status = NT_STATUS_CONNECTION_DISCONNECTED;
285                                 break;
286                 }
287                 return status;
288         }       
289
290         *sendlen = len;
291
292         return NT_STATUS_OK;
293 }
294
295 static NTSTATUS ipv4_tcp_set_option(struct socket_context *sock, const char *option, const char *val)
296 {
297         set_socket_options(sock->fd, option);
298         return NT_STATUS_OK;
299 }
300
301 static char *ipv4_tcp_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
302 {
303         struct sockaddr_in peer_addr;
304         socklen_t len = sizeof(peer_addr);
305         struct hostent *he;
306         int ret;
307
308         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
309         if (ret == -1) {
310                 return NULL;
311         }
312
313         he = gethostbyaddr((char *)&peer_addr.sin_addr, sizeof(peer_addr.sin_addr), AF_INET);
314         if (he == NULL) {
315                 return NULL;
316         }
317
318         return talloc_strdup(mem_ctx, he->h_name);
319 }
320
321 static char *ipv4_tcp_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
322 {
323         struct sockaddr_in peer_addr;
324         socklen_t len = sizeof(peer_addr);
325         int ret;
326
327         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
328         if (ret == -1) {
329                 return NULL;
330         }
331
332         return talloc_strdup(mem_ctx, inet_ntoa(peer_addr.sin_addr));
333 }
334
335 static int ipv4_tcp_get_peer_port(struct socket_context *sock)
336 {
337         struct sockaddr_in peer_addr;
338         socklen_t len = sizeof(peer_addr);
339         int ret;
340
341         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
342         if (ret == -1) {
343                 return -1;
344         }
345
346         return ntohs(peer_addr.sin_port);
347 }
348
349 static char *ipv4_tcp_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
350 {
351         struct sockaddr_in my_addr;
352         socklen_t len = sizeof(my_addr);
353         int ret;
354
355         ret = getsockname(sock->fd, (struct sockaddr *)&my_addr, &len);
356         if (ret == -1) {
357                 return NULL;
358         }
359
360         return talloc_strdup(mem_ctx, inet_ntoa(my_addr.sin_addr));
361 }
362
363 static int ipv4_tcp_get_my_port(struct socket_context *sock)
364 {
365         struct sockaddr_in my_addr;
366         socklen_t len = sizeof(my_addr);
367         int ret;
368
369         ret = getsockname(sock->fd, (struct sockaddr *)&my_addr, &len);
370         if (ret == -1) {
371                 return -1;
372         }
373
374         return ntohs(my_addr.sin_port);
375 }
376
377 static int ipv4_tcp_get_fd(struct socket_context *sock)
378 {
379         return sock->fd;
380 }
381
382 static const struct socket_ops ipv4_tcp_ops = {
383         .name           = "ipv4",
384         .type           = SOCKET_TYPE_STREAM,
385
386         .init           = ipv4_tcp_init,
387         .connect        = ipv4_tcp_connect,
388         .listen         = ipv4_tcp_listen,
389         .accept         = ipv4_tcp_accept,
390         .recv           = ipv4_tcp_recv,
391         .send           = ipv4_tcp_send,
392         .close          = ipv4_tcp_close,
393
394         .set_option     = ipv4_tcp_set_option,
395
396         .get_peer_name  = ipv4_tcp_get_peer_name,
397         .get_peer_addr  = ipv4_tcp_get_peer_addr,
398         .get_peer_port  = ipv4_tcp_get_peer_port,
399         .get_my_addr    = ipv4_tcp_get_my_addr,
400         .get_my_port    = ipv4_tcp_get_my_port,
401
402         .get_fd         = ipv4_tcp_get_fd
403 };
404
405 const struct socket_ops *socket_ipv4_ops(void)
406 {
407         return &ipv4_tcp_ops;
408 }
409
410 NTSTATUS socket_ipv4_init(void)
411 {
412         return NT_STATUS_OK;
413 }