r17586: merge lib/netif into lib/socket and use -lnsl -lsocket on the
[sfrench/samba-autobuild/.git] / source4 / lib / socket / socket.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Socket functions
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) Tim Potter      2000-2001
6    Copyright (C) Stefan Metzmacher 2004
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "lib/socket/socket.h"
25 #include "system/filesys.h"
26 #include "system/network.h"
27
28 /*
29   auto-close sockets on free
30 */
31 static int socket_destructor(struct socket_context *sock)
32 {
33         if (sock->ops->fn_close) {
34                 sock->ops->fn_close(sock);
35         }
36         return 0;
37 }
38
39 _PUBLIC_ NTSTATUS socket_create_with_ops(TALLOC_CTX *mem_ctx, const struct socket_ops *ops,
40                                          struct socket_context **new_sock, 
41                                          enum socket_type type, uint32_t flags)
42 {
43         NTSTATUS status;
44
45         (*new_sock) = talloc(mem_ctx, struct socket_context);
46         if (!(*new_sock)) {
47                 return NT_STATUS_NO_MEMORY;
48         }
49
50         (*new_sock)->type = type;
51         (*new_sock)->state = SOCKET_STATE_UNDEFINED;
52         (*new_sock)->flags = flags;
53
54         (*new_sock)->fd = -1;
55
56         (*new_sock)->private_data = NULL;
57         (*new_sock)->ops = ops;
58         (*new_sock)->backend_name = NULL;
59
60         status = (*new_sock)->ops->fn_init((*new_sock));
61         if (!NT_STATUS_IS_OK(status)) {
62                 talloc_free(*new_sock);
63                 return status;
64         }
65
66         /* by enabling "testnonblock" mode, all socket receive and
67            send calls on non-blocking sockets will randomly recv/send
68            less data than requested */
69
70         if (!(flags & SOCKET_FLAG_BLOCK) &&
71             type == SOCKET_TYPE_STREAM &&
72             lp_parm_bool(-1, "socket", "testnonblock", False)) {
73                 (*new_sock)->flags |= SOCKET_FLAG_TESTNONBLOCK;
74         }
75
76         /* we don't do a connect() on dgram sockets, so need to set
77            non-blocking at socket create time */
78         if (!(flags & SOCKET_FLAG_BLOCK) && type == SOCKET_TYPE_DGRAM) {
79                 set_blocking(socket_get_fd(*new_sock), False);
80         }
81
82         talloc_set_destructor(*new_sock, socket_destructor);
83
84         return NT_STATUS_OK;
85 }
86
87 _PUBLIC_ NTSTATUS socket_create(const char *name, enum socket_type type, 
88                                 struct socket_context **new_sock, uint32_t flags)
89 {
90         const struct socket_ops *ops;
91
92         ops = socket_getops_byname(name, type);
93         if (!ops) {
94                 return NT_STATUS_INVALID_PARAMETER;
95         }
96
97         return socket_create_with_ops(NULL, ops, new_sock, type, flags);
98 }
99
100 _PUBLIC_ NTSTATUS socket_connect(struct socket_context *sock,
101                                  const struct socket_address *my_address, 
102                                  const struct socket_address *server_address,
103                                  uint32_t flags)
104 {
105         if (sock == NULL) {
106                 return NT_STATUS_CONNECTION_DISCONNECTED;
107         }
108         if (sock->state != SOCKET_STATE_UNDEFINED) {
109                 return NT_STATUS_INVALID_PARAMETER;
110         }
111
112         if (!sock->ops->fn_connect) {
113                 return NT_STATUS_NOT_IMPLEMENTED;
114         }
115
116         return sock->ops->fn_connect(sock, my_address, server_address, flags);
117 }
118
119 _PUBLIC_ NTSTATUS socket_connect_complete(struct socket_context *sock, uint32_t flags)
120 {
121         if (!sock->ops->fn_connect_complete) {
122                 return NT_STATUS_NOT_IMPLEMENTED;
123         }
124         return sock->ops->fn_connect_complete(sock, flags);
125 }
126
127 _PUBLIC_ NTSTATUS socket_listen(struct socket_context *sock, 
128                                 const struct socket_address *my_address, 
129                                 int queue_size, uint32_t flags)
130 {
131         if (sock == NULL) {
132                 return NT_STATUS_CONNECTION_DISCONNECTED;
133         }
134         if (sock->state != SOCKET_STATE_UNDEFINED) {
135                 return NT_STATUS_INVALID_PARAMETER;
136         }
137
138         if (!sock->ops->fn_listen) {
139                 return NT_STATUS_NOT_IMPLEMENTED;
140         }
141
142         return sock->ops->fn_listen(sock, my_address, queue_size, flags);
143 }
144
145 _PUBLIC_ NTSTATUS socket_accept(struct socket_context *sock, struct socket_context **new_sock)
146 {
147         NTSTATUS status;
148
149         if (sock == NULL) {
150                 return NT_STATUS_CONNECTION_DISCONNECTED;
151         }
152         if (sock->type != SOCKET_TYPE_STREAM) {
153                 return NT_STATUS_INVALID_PARAMETER;
154         }
155
156         if (sock->state != SOCKET_STATE_SERVER_LISTEN) {
157                 return NT_STATUS_INVALID_PARAMETER;
158         }
159
160         if (!sock->ops->fn_accept) {
161                 return NT_STATUS_NOT_IMPLEMENTED;
162         }
163
164         status = sock->ops->fn_accept(sock, new_sock);
165
166         if (NT_STATUS_IS_OK(status)) {
167                 talloc_set_destructor(*new_sock, socket_destructor);
168         }
169
170         return status;
171 }
172
173 _PUBLIC_ NTSTATUS socket_recv(struct socket_context *sock, void *buf, 
174                               size_t wantlen, size_t *nread)
175 {
176         if (sock == NULL) {
177                 return NT_STATUS_CONNECTION_DISCONNECTED;
178         }
179         if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
180             sock->state != SOCKET_STATE_SERVER_CONNECTED &&
181             sock->type  != SOCKET_TYPE_DGRAM) {
182                 return NT_STATUS_INVALID_PARAMETER;
183         }
184
185         if (!sock->ops->fn_recv) {
186                 return NT_STATUS_NOT_IMPLEMENTED;
187         }
188
189         if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK) 
190             && wantlen > 1) {
191
192                 if (random() % 10 == 0) {
193                         *nread = 0;
194                         return STATUS_MORE_ENTRIES;
195                 }
196                 return sock->ops->fn_recv(sock, buf, 1+(random() % wantlen), nread);
197         }
198         return sock->ops->fn_recv(sock, buf, wantlen, nread);
199 }
200
201 _PUBLIC_ NTSTATUS socket_recvfrom(struct socket_context *sock, void *buf, 
202                                   size_t wantlen, size_t *nread, 
203                                   TALLOC_CTX *mem_ctx, struct socket_address **src_addr)
204 {
205         if (sock == NULL) {
206                 return NT_STATUS_CONNECTION_DISCONNECTED;
207         }
208         if (sock->type != SOCKET_TYPE_DGRAM) {
209                 return NT_STATUS_INVALID_PARAMETER;
210         }
211
212         if (!sock->ops->fn_recvfrom) {
213                 return NT_STATUS_NOT_IMPLEMENTED;
214         }
215
216         return sock->ops->fn_recvfrom(sock, buf, wantlen, nread, 
217                                       mem_ctx, src_addr);
218 }
219
220 _PUBLIC_ NTSTATUS socket_send(struct socket_context *sock, 
221                               const DATA_BLOB *blob, size_t *sendlen)
222 {
223         if (sock == NULL) {
224                 return NT_STATUS_CONNECTION_DISCONNECTED;
225         }
226         if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
227             sock->state != SOCKET_STATE_SERVER_CONNECTED) {
228                 return NT_STATUS_INVALID_PARAMETER;
229         }
230
231         if (!sock->ops->fn_send) {
232                 return NT_STATUS_NOT_IMPLEMENTED;
233         }
234         
235         if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
236             && blob->length > 1) {
237                 DATA_BLOB blob2 = *blob;
238                 if (random() % 10 == 0) {
239                         *sendlen = 0;
240                         return STATUS_MORE_ENTRIES;
241                 }
242                 /* The random size sends are incompatible with TLS and SASL
243                  * sockets, which require re-sends to be consistant */
244                 if (!(sock->flags & SOCKET_FLAG_ENCRYPT)) {
245                         blob2.length = 1+(random() % blob2.length);
246                 } else {
247                         /* This is particularly stressful on buggy
248                          * LDAP clients, that don't expect on LDAP
249                          * packet in many SASL packets */
250                         blob2.length = 1 + blob2.length/2;
251                 }
252                 return sock->ops->fn_send(sock, &blob2, sendlen);
253         }
254         return sock->ops->fn_send(sock, blob, sendlen);
255 }
256
257
258 _PUBLIC_ NTSTATUS socket_sendto(struct socket_context *sock, 
259                                 const DATA_BLOB *blob, size_t *sendlen, 
260                                 const struct socket_address *dest_addr)
261 {
262         if (sock == NULL) {
263                 return NT_STATUS_CONNECTION_DISCONNECTED;
264         }
265         if (sock->type != SOCKET_TYPE_DGRAM) {
266                 return NT_STATUS_INVALID_PARAMETER;
267         }
268
269         if (sock->state == SOCKET_STATE_CLIENT_CONNECTED ||
270             sock->state == SOCKET_STATE_SERVER_CONNECTED) {
271                 return NT_STATUS_INVALID_PARAMETER;
272         }
273
274         if (!sock->ops->fn_sendto) {
275                 return NT_STATUS_NOT_IMPLEMENTED;
276         }
277
278         return sock->ops->fn_sendto(sock, blob, sendlen, dest_addr);
279 }
280
281
282 /*
283   ask for the number of bytes in a pending incoming packet
284 */
285 _PUBLIC_ NTSTATUS socket_pending(struct socket_context *sock, size_t *npending)
286 {
287         if (sock == NULL) {
288                 return NT_STATUS_CONNECTION_DISCONNECTED;
289         }
290         if (!sock->ops->fn_pending) {
291                 return NT_STATUS_NOT_IMPLEMENTED;
292         }
293         return sock->ops->fn_pending(sock, npending);
294 }
295
296
297 _PUBLIC_ NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val)
298 {
299         if (sock == NULL) {
300                 return NT_STATUS_CONNECTION_DISCONNECTED;
301         }
302         if (!sock->ops->fn_set_option) {
303                 return NT_STATUS_NOT_IMPLEMENTED;
304         }
305
306         return sock->ops->fn_set_option(sock, option, val);
307 }
308
309 _PUBLIC_ char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
310 {
311         if (!sock->ops->fn_get_peer_name) {
312                 return NULL;
313         }
314
315         return sock->ops->fn_get_peer_name(sock, mem_ctx);
316 }
317
318 _PUBLIC_ struct socket_address *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
319 {
320         if (!sock->ops->fn_get_peer_addr) {
321                 return NULL;
322         }
323
324         return sock->ops->fn_get_peer_addr(sock, mem_ctx);
325 }
326
327 _PUBLIC_ struct socket_address *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
328 {
329         if (!sock->ops->fn_get_my_addr) {
330                 return NULL;
331         }
332
333         return sock->ops->fn_get_my_addr(sock, mem_ctx);
334 }
335
336 _PUBLIC_ int socket_get_fd(struct socket_context *sock)
337 {
338         if (!sock->ops->fn_get_fd) {
339                 return -1;
340         }
341
342         return sock->ops->fn_get_fd(sock);
343 }
344
345 /*
346   call dup() on a socket, and close the old fd. This is used to change
347   the fd to the lowest available number, to make select() more
348   efficient (select speed depends on the maxiumum fd number passed to
349   it)
350 */
351 _PUBLIC_ NTSTATUS socket_dup(struct socket_context *sock)
352 {
353         int fd;
354         if (sock->fd == -1) {
355                 return NT_STATUS_INVALID_HANDLE;
356         }
357         fd = dup(sock->fd);
358         if (fd == -1) {
359                 return map_nt_error_from_unix(errno);
360         }
361         close(sock->fd);
362         sock->fd = fd;
363         return NT_STATUS_OK;
364         
365 }
366
367 /* Create a new socket_address.  The type must match the socket type.
368  * The host parameter may be an IP or a hostname 
369  */
370
371 _PUBLIC_ struct socket_address *socket_address_from_strings(TALLOC_CTX *mem_ctx,
372                                                             const char *family,
373                                                             const char *host,
374                                                             int port)
375 {
376         struct socket_address *addr = talloc(mem_ctx, struct socket_address);
377         if (!addr) {
378                 return NULL;
379         }
380
381         addr->family = family;
382         addr->addr = talloc_strdup(addr, host);
383         if (!addr->addr) {
384                 talloc_free(addr);
385                 return NULL;
386         }
387         addr->port = port;
388         addr->sockaddr = NULL;
389         addr->sockaddrlen = 0;
390
391         return addr;
392 }
393
394 /* Create a new socket_address.  Copy the struct sockaddr into the new
395  * structure.  Used for hooks in the kerberos libraries, where they
396  * supply only a struct sockaddr */
397
398 _PUBLIC_ struct socket_address *socket_address_from_sockaddr(TALLOC_CTX *mem_ctx, 
399                                                              struct sockaddr *sockaddr, 
400                                                              size_t sockaddrlen)
401 {
402         struct socket_address *addr = talloc(mem_ctx, struct socket_address);
403         if (!addr) {
404                 return NULL;
405         }
406         addr->family = NULL; 
407         addr->addr = NULL;
408         addr->port = 0;
409         addr->sockaddr = talloc_memdup(addr, sockaddr, sockaddrlen);
410         if (!addr->sockaddr) {
411                 talloc_free(addr);
412                 return NULL;
413         }
414         addr->sockaddrlen = sockaddrlen;
415         return addr;
416 }
417
418 _PUBLIC_ const struct socket_ops *socket_getops_byname(const char *family, enum socket_type type)
419 {
420         extern const struct socket_ops *socket_ipv4_ops(enum socket_type);
421         extern const struct socket_ops *socket_ipv6_ops(enum socket_type);
422         extern const struct socket_ops *socket_unixdom_ops(enum socket_type);
423
424         if (strcmp("ip", family) == 0 || 
425             strcmp("ipv4", family) == 0) {
426                 return socket_ipv4_ops(type);
427         }
428
429 #if HAVE_SOCKET_IPV6
430         if (strcmp("ipv6", family) == 0) {
431                 if (lp_parm_bool(-1, "socket", "noipv6", False)) {
432                         DEBUG(3, ("IPv6 support was disabled in smb.conf"));
433                         return NULL;
434                 }
435                 return socket_ipv6_ops(type);
436         }
437 #endif
438
439         if (strcmp("unix", family) == 0) {
440                 return socket_unixdom_ops(type);
441         }
442
443         return NULL;
444 }
445
446 enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
447
448 static const struct {
449         const char *name;
450         int level;
451         int option;
452         int value;
453         int opttype;
454 } socket_options[] = {
455   {"SO_KEEPALIVE",      SOL_SOCKET,    SO_KEEPALIVE,    0,                 OPT_BOOL},
456   {"SO_REUSEADDR",      SOL_SOCKET,    SO_REUSEADDR,    0,                 OPT_BOOL},
457   {"SO_BROADCAST",      SOL_SOCKET,    SO_BROADCAST,    0,                 OPT_BOOL},
458 #ifdef TCP_NODELAY
459   {"TCP_NODELAY",       IPPROTO_TCP,   TCP_NODELAY,     0,                 OPT_BOOL},
460 #endif
461 #ifdef IPTOS_LOWDELAY
462   {"IPTOS_LOWDELAY",    IPPROTO_IP,    IP_TOS,          IPTOS_LOWDELAY,    OPT_ON},
463 #endif
464 #ifdef IPTOS_THROUGHPUT
465   {"IPTOS_THROUGHPUT",  IPPROTO_IP,    IP_TOS,          IPTOS_THROUGHPUT,  OPT_ON},
466 #endif
467 #ifdef SO_REUSEPORT
468   {"SO_REUSEPORT",      SOL_SOCKET,    SO_REUSEPORT,    0,                 OPT_BOOL},
469 #endif
470 #ifdef SO_SNDBUF
471   {"SO_SNDBUF",         SOL_SOCKET,    SO_SNDBUF,       0,                 OPT_INT},
472 #endif
473 #ifdef SO_RCVBUF
474   {"SO_RCVBUF",         SOL_SOCKET,    SO_RCVBUF,       0,                 OPT_INT},
475 #endif
476 #ifdef SO_SNDLOWAT
477   {"SO_SNDLOWAT",       SOL_SOCKET,    SO_SNDLOWAT,     0,                 OPT_INT},
478 #endif
479 #ifdef SO_RCVLOWAT
480   {"SO_RCVLOWAT",       SOL_SOCKET,    SO_RCVLOWAT,     0,                 OPT_INT},
481 #endif
482 #ifdef SO_SNDTIMEO
483   {"SO_SNDTIMEO",       SOL_SOCKET,    SO_SNDTIMEO,     0,                 OPT_INT},
484 #endif
485 #ifdef SO_RCVTIMEO
486   {"SO_RCVTIMEO",       SOL_SOCKET,    SO_RCVTIMEO,     0,                 OPT_INT},
487 #endif
488   {NULL,0,0,0,0}};
489
490
491 /**
492  Set user socket options.
493 **/
494 _PUBLIC_ void set_socket_options(int fd, const char *options)
495 {
496         const char **options_list = str_list_make(NULL, options, " \t,");
497         int j;
498
499         if (!options_list)
500                 return;
501
502         for (j = 0; options_list[j]; j++) {
503                 const char *tok = options_list[j];
504                 int ret=0,i;
505                 int value = 1;
506                 char *p;
507                 BOOL got_value = False;
508
509                 if ((p = strchr(tok,'='))) {
510                         *p = 0;
511                         value = atoi(p+1);
512                         got_value = True;
513                 }
514
515                 for (i=0;socket_options[i].name;i++)
516                         if (strequal(socket_options[i].name,tok))
517                                 break;
518
519                 if (!socket_options[i].name) {
520                         DEBUG(0,("Unknown socket option %s\n",tok));
521                         continue;
522                 }
523
524                 switch (socket_options[i].opttype) {
525                 case OPT_BOOL:
526                 case OPT_INT:
527                         ret = setsockopt(fd,socket_options[i].level,
528                                                 socket_options[i].option,(char *)&value,sizeof(int));
529                         break;
530
531                 case OPT_ON:
532                         if (got_value)
533                                 DEBUG(0,("syntax error - %s does not take a value\n",tok));
534
535                         {
536                                 int on = socket_options[i].value;
537                                 ret = setsockopt(fd,socket_options[i].level,
538                                                         socket_options[i].option,(char *)&on,sizeof(int));
539                         }
540                         break;    
541                 }
542       
543                 if (ret != 0)
544                         DEBUG(0,("Failed to set socket option %s (Error %s)\n",tok, strerror(errno) ));
545         }
546
547         talloc_free(options_list);
548 }
549