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