r5108: the beginnings of a nbtd server for Samba4. Currently just displays
[samba.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
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         socket_set_option(sock, "SO_REUSEADDR=1", NULL);
143
144         ip_addr = interpret_addr2(my_address);
145
146         ZERO_STRUCT(my_addr);
147 #ifdef HAVE_SOCK_SIN_LEN
148         my_addr.sin_len         = sizeof(my_addr);
149 #endif
150         my_addr.sin_addr.s_addr = ip_addr.addr;
151         my_addr.sin_port        = htons(port);
152         my_addr.sin_family      = PF_INET;
153
154         ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
155         if (ret == -1) {
156                 return map_nt_error_from_unix(errno);
157         }
158
159         if (sock->type == SOCKET_TYPE_STREAM) {
160                 ret = listen(sock->fd, queue_size);
161                 if (ret == -1) {
162                         return map_nt_error_from_unix(errno);
163                 }
164         }
165
166         if (!(flags & SOCKET_FLAG_BLOCK)) {
167                 ret = set_blocking(sock->fd, False);
168                 if (ret == -1) {
169                         return map_nt_error_from_unix(errno);
170                 }
171         }
172
173         sock->state= SOCKET_STATE_SERVER_LISTEN;
174
175         return NT_STATUS_OK;
176 }
177
178 static NTSTATUS ipv4_accept(struct socket_context *sock, struct socket_context **new_sock)
179 {
180         struct sockaddr_in cli_addr;
181         socklen_t cli_addr_len = sizeof(cli_addr);
182         int new_fd;
183
184         if (sock->type != SOCKET_TYPE_STREAM) {
185                 return NT_STATUS_INVALID_PARAMETER;
186         }
187
188         new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
189         if (new_fd == -1) {
190                 return map_nt_error_from_unix(errno);
191         }
192
193         if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
194                 int ret = set_blocking(new_fd, False);
195                 if (ret == -1) {
196                         close(new_fd);
197                         return map_nt_error_from_unix(errno);
198                 }
199         }
200
201         /* TODO: we could add a 'accept_check' hook here
202          *       which get the black/white lists via socket_set_accept_filter()
203          *       or something like that
204          *       --metze
205          */
206
207         (*new_sock) = talloc(NULL, struct socket_context);
208         if (!(*new_sock)) {
209                 close(new_fd);
210                 return NT_STATUS_NO_MEMORY;
211         }
212
213         /* copy the socket_context */
214         (*new_sock)->type               = sock->type;
215         (*new_sock)->state              = SOCKET_STATE_SERVER_CONNECTED;
216         (*new_sock)->flags              = sock->flags;
217
218         (*new_sock)->fd                 = new_fd;
219
220         (*new_sock)->private_data       = NULL;
221         (*new_sock)->ops                = sock->ops;
222
223         return NT_STATUS_OK;
224 }
225
226 static NTSTATUS ipv4_recv(struct socket_context *sock, void *buf, 
227                               size_t wantlen, size_t *nread, uint32_t flags)
228 {
229         ssize_t gotlen;
230         int flgs = 0;
231
232         /* TODO: we need to map all flags here */
233         if (flags & SOCKET_FLAG_PEEK) {
234                 flgs |= MSG_PEEK;
235         }
236
237         if (flags & SOCKET_FLAG_BLOCK) {
238                 flgs |= MSG_WAITALL;
239         }
240
241         *nread = 0;
242
243         gotlen = recv(sock->fd, buf, wantlen, flgs);
244         if (gotlen == 0) {
245                 return NT_STATUS_END_OF_FILE;
246         } else if (gotlen == -1) {
247                 return map_nt_error_from_unix(errno);
248         }
249
250         *nread = gotlen;
251
252         return NT_STATUS_OK;
253 }
254
255
256 static NTSTATUS ipv4_recvfrom(struct socket_context *sock, void *buf, 
257                               size_t wantlen, size_t *nread, uint32_t flags,
258                               const char **src_addr, int *src_port)
259 {
260         ssize_t gotlen;
261         int flgs = 0;
262         struct sockaddr_in from_addr;
263         socklen_t from_len = sizeof(from_addr);
264         const char *addr;
265
266         if (flags & SOCKET_FLAG_PEEK) {
267                 flgs |= MSG_PEEK;
268         }
269
270         if (flags & SOCKET_FLAG_BLOCK) {
271                 flgs |= MSG_WAITALL;
272         }
273
274         *nread = 0;
275
276         gotlen = recvfrom(sock->fd, buf, wantlen, flgs, 
277                           (struct sockaddr *)&from_addr, &from_len);
278         if (gotlen == 0) {
279                 return NT_STATUS_END_OF_FILE;
280         } else if (gotlen == -1) {
281                 return map_nt_error_from_unix(errno);
282         }
283
284         addr = inet_ntoa(from_addr.sin_addr);
285         if (addr == NULL) {
286                 return NT_STATUS_INTERNAL_ERROR;
287         }
288         *src_addr = talloc_strdup(sock, addr);
289         NT_STATUS_HAVE_NO_MEMORY(*src_addr);
290         *src_port = ntohs(from_addr.sin_port);
291
292         *nread = gotlen;
293
294         return NT_STATUS_OK;
295 }
296
297 static NTSTATUS ipv4_send(struct socket_context *sock, 
298                               const DATA_BLOB *blob, size_t *sendlen, uint32_t flags)
299 {
300         ssize_t len;
301         int flgs = 0;
302
303         *sendlen = 0;
304
305         len = send(sock->fd, blob->data, blob->length, flgs);
306         if (len == -1) {
307                 return map_nt_error_from_unix(errno);
308         }       
309
310         *sendlen = len;
311
312         return NT_STATUS_OK;
313 }
314
315 static NTSTATUS ipv4_sendto(struct socket_context *sock, 
316                             const DATA_BLOB *blob, size_t *sendlen, uint32_t flags,
317                             const char *dest_addr, int dest_port)
318 {
319         ssize_t len;
320         int flgs = 0;
321         struct sockaddr_in srv_addr;
322         struct ipv4_addr addr;
323
324         ZERO_STRUCT(srv_addr);
325 #ifdef HAVE_SOCK_SIN_LEN
326         srv_addr.sin_len         = sizeof(srv_addr);
327 #endif
328         addr                     = interpret_addr2(dest_addr);
329         srv_addr.sin_addr.s_addr = addr.addr;
330         srv_addr.sin_port        = htons(dest_port);
331         srv_addr.sin_family      = PF_INET;
332
333         *sendlen = 0;
334
335         len = sendto(sock->fd, blob->data, blob->length, flgs, 
336                    (struct sockaddr *)&srv_addr, sizeof(srv_addr));
337         if (len == -1) {
338                 return map_nt_error_from_unix(errno);
339         }       
340
341         *sendlen = len;
342
343         return NT_STATUS_OK;
344 }
345
346 static NTSTATUS ipv4_set_option(struct socket_context *sock, const char *option, const char *val)
347 {
348         set_socket_options(sock->fd, option);
349         return NT_STATUS_OK;
350 }
351
352 static char *ipv4_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
353 {
354         struct sockaddr_in peer_addr;
355         socklen_t len = sizeof(peer_addr);
356         struct hostent *he;
357         int ret;
358
359         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
360         if (ret == -1) {
361                 return NULL;
362         }
363
364         he = gethostbyaddr((char *)&peer_addr.sin_addr, sizeof(peer_addr.sin_addr), AF_INET);
365         if (he == NULL) {
366                 return NULL;
367         }
368
369         return talloc_strdup(mem_ctx, he->h_name);
370 }
371
372 static char *ipv4_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
373 {
374         struct sockaddr_in peer_addr;
375         socklen_t len = sizeof(peer_addr);
376         int ret;
377
378         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
379         if (ret == -1) {
380                 return NULL;
381         }
382
383         return talloc_strdup(mem_ctx, inet_ntoa(peer_addr.sin_addr));
384 }
385
386 static int ipv4_get_peer_port(struct socket_context *sock)
387 {
388         struct sockaddr_in peer_addr;
389         socklen_t len = sizeof(peer_addr);
390         int ret;
391
392         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
393         if (ret == -1) {
394                 return -1;
395         }
396
397         return ntohs(peer_addr.sin_port);
398 }
399
400 static char *ipv4_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
401 {
402         struct sockaddr_in my_addr;
403         socklen_t len = sizeof(my_addr);
404         int ret;
405
406         ret = getsockname(sock->fd, (struct sockaddr *)&my_addr, &len);
407         if (ret == -1) {
408                 return NULL;
409         }
410
411         return talloc_strdup(mem_ctx, inet_ntoa(my_addr.sin_addr));
412 }
413
414 static int ipv4_get_my_port(struct socket_context *sock)
415 {
416         struct sockaddr_in my_addr;
417         socklen_t len = sizeof(my_addr);
418         int ret;
419
420         ret = getsockname(sock->fd, (struct sockaddr *)&my_addr, &len);
421         if (ret == -1) {
422                 return -1;
423         }
424
425         return ntohs(my_addr.sin_port);
426 }
427
428 static int ipv4_get_fd(struct socket_context *sock)
429 {
430         return sock->fd;
431 }
432
433 static const struct socket_ops ipv4_ops = {
434         .name                   = "ipv4",
435         .fn_init                = ipv4_init,
436         .fn_connect             = ipv4_connect,
437         .fn_connect_complete    = ipv4_connect_complete,
438         .fn_listen              = ipv4_listen,
439         .fn_accept              = ipv4_accept,
440         .fn_recv                = ipv4_recv,
441         .fn_recvfrom            = ipv4_recvfrom,
442         .fn_send                = ipv4_send,
443         .fn_sendto              = ipv4_sendto,
444         .fn_close               = ipv4_close,
445
446         .fn_set_option          = ipv4_set_option,
447
448         .fn_get_peer_name       = ipv4_get_peer_name,
449         .fn_get_peer_addr       = ipv4_get_peer_addr,
450         .fn_get_peer_port       = ipv4_get_peer_port,
451         .fn_get_my_addr         = ipv4_get_my_addr,
452         .fn_get_my_port         = ipv4_get_my_port,
453
454         .fn_get_fd              = ipv4_get_fd
455 };
456
457 const struct socket_ops *socket_ipv4_ops(enum socket_type type)
458 {
459         return &ipv4_ops;
460 }