f280f804ef8aed417a75fc39fe61e2f2066779ab
[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                 return map_nt_error_from_unix(errno);
28         }
29
30         return NT_STATUS_OK;
31 }
32
33 static void ipv4_tcp_close(struct socket_context *sock)
34 {
35         close(sock->fd);
36 }
37
38 static NTSTATUS ipv4_tcp_connect(struct socket_context *sock,
39                                  const char *my_address, int my_port,
40                                  const char *srv_address, int srv_port,
41                                  uint32_t flags)
42 {
43         struct sockaddr_in srv_addr;
44         struct in_addr my_ip;
45         struct in_addr srv_ip;
46         int ret;
47
48         my_ip = interpret_addr2(my_address);
49
50         if (my_ip.s_addr != 0 || my_port != 0) {
51                 struct sockaddr_in my_addr;
52                 ZERO_STRUCT(my_addr);
53 #ifdef HAVE_SOCK_SIN_LEN
54                 my_addr.sin_len         = sizeof(my_addr);
55 #endif
56                 my_addr.sin_addr        = my_ip;
57                 my_addr.sin_port        = htons(my_port);
58                 my_addr.sin_family      = PF_INET;
59                 
60                 ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
61                 if (ret == -1) {
62                         return map_nt_error_from_unix(errno);
63                 }
64         }
65
66         srv_ip = interpret_addr2(srv_address);
67
68         ZERO_STRUCT(srv_addr);
69 #ifdef HAVE_SOCK_SIN_LEN
70         srv_addr.sin_len        = sizeof(srv_addr);
71 #endif
72         srv_addr.sin_addr       = srv_ip;
73         srv_addr.sin_port       = htons(srv_port);
74         srv_addr.sin_family     = PF_INET;
75
76         ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
77         if (ret == -1) {
78                 return map_nt_error_from_unix(errno);
79         }
80
81         if (!(flags & SOCKET_FLAG_BLOCK)) {
82                 ret = set_blocking(sock->fd, False);
83                 if (ret == -1) {
84                         return map_nt_error_from_unix(errno);
85                 }
86         }
87
88         sock->state = SOCKET_STATE_CLIENT_CONNECTED;
89
90         return NT_STATUS_OK;
91 }
92
93 static NTSTATUS ipv4_tcp_listen(struct socket_context *sock,
94                                         const char *my_address, int port,
95                                         int queue_size, uint32_t flags)
96 {
97         struct sockaddr_in my_addr;
98         struct in_addr ip_addr;
99         int ret;
100
101         ip_addr = interpret_addr2(my_address);
102
103         ZERO_STRUCT(my_addr);
104 #ifdef HAVE_SOCK_SIN_LEN
105         my_addr.sin_len         = sizeof(my_addr);
106 #endif
107         my_addr.sin_addr        = ip_addr;
108         my_addr.sin_port        = htons(port);
109         my_addr.sin_family      = PF_INET;
110
111         ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
112         if (ret == -1) {
113                 return map_nt_error_from_unix(errno);
114         }
115
116         ret = listen(sock->fd, queue_size);
117         if (ret == -1) {
118                 return map_nt_error_from_unix(errno);
119         }
120
121         if (!(flags & SOCKET_FLAG_BLOCK)) {
122                 ret = set_blocking(sock->fd, False);
123                 if (ret == -1) {
124                         return map_nt_error_from_unix(errno);
125                 }
126         }
127
128         sock->state= SOCKET_STATE_SERVER_LISTEN;
129
130         return NT_STATUS_OK;
131 }
132
133 static NTSTATUS ipv4_tcp_accept(struct socket_context *sock, struct socket_context **new_sock, uint32_t flags)
134 {
135         struct sockaddr_in cli_addr;
136         socklen_t cli_addr_len = sizeof(cli_addr);
137         int new_fd;
138
139         new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
140         if (new_fd == -1) {
141                 return map_nt_error_from_unix(errno);
142         }
143
144         /* TODO: we could add a 'accept_check' hook here
145          *       which get the black/white lists via socket_set_accept_filter()
146          *       or something like that
147          *       --metze
148          */
149
150         (*new_sock) = talloc_p(NULL, struct socket_context);
151         if (!(*new_sock)) {
152                 close(new_fd);
153                 return NT_STATUS_NO_MEMORY;
154         }
155
156         /* copy the socket_context */
157         (*new_sock)->type               = sock->type;
158         (*new_sock)->state              = SOCKET_STATE_SERVER_CONNECTED;
159         (*new_sock)->flags              = flags;
160
161         (*new_sock)->fd                 = new_fd;
162
163         (*new_sock)->private_data       = NULL;
164         (*new_sock)->ops                = sock->ops;
165
166         return NT_STATUS_OK;
167 }
168
169 static NTSTATUS ipv4_tcp_recv(struct socket_context *sock, TALLOC_CTX *mem_ctx,
170                               DATA_BLOB *blob, size_t wantlen, uint32_t flags)
171 {
172         ssize_t gotlen;
173         void *buf;
174         int flgs = 0;
175
176         buf = talloc(mem_ctx, wantlen);
177         if (!buf) {
178                 return NT_STATUS_NO_MEMORY;
179         }
180
181         /* TODO: we need to map all flags here */
182         if (flags & SOCKET_FLAG_PEEK) {
183                 flgs |= MSG_PEEK;
184         }
185
186         if (flags & SOCKET_FLAG_BLOCK) {
187                 flgs |= MSG_WAITALL;
188         }
189
190         gotlen = recv(sock->fd, buf, wantlen, flgs);
191         if (gotlen == 0) {
192                 talloc_free(buf);
193                 return NT_STATUS_END_OF_FILE;
194         } else if (gotlen == -1) {
195                 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
196                 switch (errno) {
197                         case EBADF:
198                         case ENOTCONN:
199                         case ENOTSOCK:
200                         case EFAULT:
201                         case EINVAL:
202                                 status = NT_STATUS_INVALID_PARAMETER;
203                                 break;
204                         case EAGAIN:
205                         case EINTR:
206                                 status = STATUS_MORE_ENTRIES;
207                                 break;
208                         case ECONNREFUSED:
209                                 status = NT_STATUS_CONNECTION_REFUSED;
210                                 break;
211                 }
212                 talloc_free(buf);
213                 return status;
214         }
215
216         blob->length = gotlen;
217         blob->data = talloc_realloc(mem_ctx, buf, gotlen);
218         if (!blob->data) {
219                 return NT_STATUS_NO_MEMORY;
220         }
221
222         return NT_STATUS_OK;
223 }
224
225 static NTSTATUS ipv4_tcp_send(struct socket_context *sock, TALLOC_CTX *mem_ctx,
226                               const DATA_BLOB *blob, size_t *sendlen, uint32_t flags)
227 {
228         ssize_t len;
229         int flgs = 0;
230
231         *sendlen = 0;
232
233         len = send(sock->fd, blob->data, blob->length, flgs);
234         if (len == -1) {
235                 return map_nt_error_from_unix(errno);
236         }       
237
238         *sendlen = len;
239
240         return NT_STATUS_OK;
241 }
242
243 static NTSTATUS ipv4_tcp_set_option(struct socket_context *sock, const char *option, const char *val)
244 {
245         set_socket_options(sock->fd, option);
246         return NT_STATUS_OK;
247 }
248
249 static char *ipv4_tcp_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
250 {
251         struct sockaddr_in peer_addr;
252         socklen_t len = sizeof(peer_addr);
253         struct hostent *he;
254         int ret;
255
256         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
257         if (ret == -1) {
258                 return NULL;
259         }
260
261         he = gethostbyaddr((char *)&peer_addr.sin_addr, sizeof(peer_addr.sin_addr), AF_INET);
262         if (he == NULL) {
263                 return NULL;
264         }
265
266         return talloc_strdup(mem_ctx, he->h_name);
267 }
268
269 static char *ipv4_tcp_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
270 {
271         struct sockaddr_in peer_addr;
272         socklen_t len = sizeof(peer_addr);
273         int ret;
274
275         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
276         if (ret == -1) {
277                 return NULL;
278         }
279
280         return talloc_strdup(mem_ctx, inet_ntoa(peer_addr.sin_addr));
281 }
282
283 static int ipv4_tcp_get_peer_port(struct socket_context *sock)
284 {
285         struct sockaddr_in peer_addr;
286         socklen_t len = sizeof(peer_addr);
287         int ret;
288
289         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
290         if (ret == -1) {
291                 return -1;
292         }
293
294         return ntohs(peer_addr.sin_port);
295 }
296
297 static char *ipv4_tcp_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
298 {
299         struct sockaddr_in my_addr;
300         socklen_t len = sizeof(my_addr);
301         int ret;
302
303         ret = getsockname(sock->fd, (struct sockaddr *)&my_addr, &len);
304         if (ret == -1) {
305                 return NULL;
306         }
307
308         return talloc_strdup(mem_ctx, inet_ntoa(my_addr.sin_addr));
309 }
310
311 static int ipv4_tcp_get_my_port(struct socket_context *sock)
312 {
313         struct sockaddr_in my_addr;
314         socklen_t len = sizeof(my_addr);
315         int ret;
316
317         ret = getsockname(sock->fd, (struct sockaddr *)&my_addr, &len);
318         if (ret == -1) {
319                 return -1;
320         }
321
322         return ntohs(my_addr.sin_port);
323 }
324
325 static int ipv4_tcp_get_fd(struct socket_context *sock)
326 {
327         return sock->fd;
328 }
329
330 static const struct socket_ops ipv4_tcp_ops = {
331         .name           = "ipv4",
332         .type           = SOCKET_TYPE_STREAM,
333
334         .init           = ipv4_tcp_init,
335         .connect        = ipv4_tcp_connect,
336         .listen         = ipv4_tcp_listen,
337         .accept         = ipv4_tcp_accept,
338         .recv           = ipv4_tcp_recv,
339         .send           = ipv4_tcp_send,
340         .close          = ipv4_tcp_close,
341
342         .set_option     = ipv4_tcp_set_option,
343
344         .get_peer_name  = ipv4_tcp_get_peer_name,
345         .get_peer_addr  = ipv4_tcp_get_peer_addr,
346         .get_peer_port  = ipv4_tcp_get_peer_port,
347         .get_my_addr    = ipv4_tcp_get_my_addr,
348         .get_my_port    = ipv4_tcp_get_my_port,
349
350         .get_fd         = ipv4_tcp_get_fd
351 };
352
353 const struct socket_ops *socket_ipv4_ops(void)
354 {
355         return &ipv4_tcp_ops;
356 }
357
358 NTSTATUS socket_ipv4_init(void)
359 {
360         return NT_STATUS_OK;
361 }