r15349: Integrate set_socket_options() into the socket library
[bbaumbach/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(void *ptr)
32 {
33         struct socket_context *sock = ptr;
34         if (sock->ops->fn_close) {
35                 sock->ops->fn_close(sock);
36         }
37         return 0;
38 }
39
40 static NTSTATUS socket_create_with_ops(TALLOC_CTX *mem_ctx, const struct socket_ops *ops,
41                                        struct socket_context **new_sock, 
42                                        enum socket_type type, uint32_t flags)
43 {
44         NTSTATUS status;
45
46         (*new_sock) = talloc(mem_ctx, struct socket_context);
47         if (!(*new_sock)) {
48                 return NT_STATUS_NO_MEMORY;
49         }
50
51         (*new_sock)->type = type;
52         (*new_sock)->state = SOCKET_STATE_UNDEFINED;
53         (*new_sock)->flags = flags;
54
55         (*new_sock)->fd = -1;
56
57         (*new_sock)->private_data = NULL;
58         (*new_sock)->ops = ops;
59         (*new_sock)->backend_name = NULL;
60
61         status = (*new_sock)->ops->fn_init((*new_sock));
62         if (!NT_STATUS_IS_OK(status)) {
63                 talloc_free(*new_sock);
64                 return status;
65         }
66
67         /* by enabling "testnonblock" mode, all socket receive and
68            send calls on non-blocking sockets will randomly recv/send
69            less data than requested */
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, uint32_t flags)
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) && wantlen > 1) {
190                 if (random() % 10 == 0) {
191                         *nread = 0;
192                         return STATUS_MORE_ENTRIES;
193                 }
194                 return sock->ops->fn_recv(sock, buf, 1+(random() % wantlen), nread, flags);
195         }
196
197         return sock->ops->fn_recv(sock, buf, wantlen, nread, flags);
198 }
199
200 _PUBLIC_ NTSTATUS socket_recvfrom(struct socket_context *sock, void *buf, 
201                                   size_t wantlen, size_t *nread, uint32_t flags,
202                                   TALLOC_CTX *mem_ctx, struct socket_address **src_addr)
203 {
204         if (sock == NULL) {
205                 return NT_STATUS_CONNECTION_DISCONNECTED;
206         }
207         if (sock->type != SOCKET_TYPE_DGRAM) {
208                 return NT_STATUS_INVALID_PARAMETER;
209         }
210
211         if (!sock->ops->fn_recvfrom) {
212                 return NT_STATUS_NOT_IMPLEMENTED;
213         }
214
215         return sock->ops->fn_recvfrom(sock, buf, wantlen, nread, flags, 
216                                       mem_ctx, src_addr);
217 }
218
219 _PUBLIC_ NTSTATUS socket_send(struct socket_context *sock, 
220                               const DATA_BLOB *blob, size_t *sendlen, uint32_t flags)
221 {
222         if (sock == NULL) {
223                 return NT_STATUS_CONNECTION_DISCONNECTED;
224         }
225         if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
226             sock->state != SOCKET_STATE_SERVER_CONNECTED) {
227                 return NT_STATUS_INVALID_PARAMETER;
228         }
229
230         if (!sock->ops->fn_send) {
231                 return NT_STATUS_NOT_IMPLEMENTED;
232         }
233
234         if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK) && blob->length > 1) {
235                 DATA_BLOB blob2 = *blob;
236                 if (random() % 10 == 0) {
237                         *sendlen = 0;
238                         return STATUS_MORE_ENTRIES;
239                 }
240                 blob2.length = 1+(random() % blob2.length);
241                 return sock->ops->fn_send(sock, &blob2, sendlen, flags);
242         }
243
244         return sock->ops->fn_send(sock, blob, sendlen, flags);
245 }
246
247
248 _PUBLIC_ NTSTATUS socket_sendto(struct socket_context *sock, 
249                                 const DATA_BLOB *blob, size_t *sendlen, uint32_t flags,
250                                 const struct socket_address *dest_addr)
251 {
252         if (sock == NULL) {
253                 return NT_STATUS_CONNECTION_DISCONNECTED;
254         }
255         if (sock->type != SOCKET_TYPE_DGRAM) {
256                 return NT_STATUS_INVALID_PARAMETER;
257         }
258
259         if (sock->state == SOCKET_STATE_CLIENT_CONNECTED ||
260             sock->state == SOCKET_STATE_SERVER_CONNECTED) {
261                 return NT_STATUS_INVALID_PARAMETER;
262         }
263
264         if (!sock->ops->fn_sendto) {
265                 return NT_STATUS_NOT_IMPLEMENTED;
266         }
267
268         return sock->ops->fn_sendto(sock, blob, sendlen, flags, dest_addr);
269 }
270
271
272 /*
273   ask for the number of bytes in a pending incoming packet
274 */
275 _PUBLIC_ NTSTATUS socket_pending(struct socket_context *sock, size_t *npending)
276 {
277         if (sock == NULL) {
278                 return NT_STATUS_CONNECTION_DISCONNECTED;
279         }
280         if (!sock->ops->fn_pending) {
281                 return NT_STATUS_NOT_IMPLEMENTED;
282         }
283         return sock->ops->fn_pending(sock, npending);
284 }
285
286
287 _PUBLIC_ NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val)
288 {
289         if (sock == NULL) {
290                 return NT_STATUS_CONNECTION_DISCONNECTED;
291         }
292         if (!sock->ops->fn_set_option) {
293                 return NT_STATUS_NOT_IMPLEMENTED;
294         }
295
296         return sock->ops->fn_set_option(sock, option, val);
297 }
298
299 _PUBLIC_ char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
300 {
301         if (!sock->ops->fn_get_peer_name) {
302                 return NULL;
303         }
304
305         return sock->ops->fn_get_peer_name(sock, mem_ctx);
306 }
307
308 _PUBLIC_ struct socket_address *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
309 {
310         if (!sock->ops->fn_get_peer_addr) {
311                 return NULL;
312         }
313
314         return sock->ops->fn_get_peer_addr(sock, mem_ctx);
315 }
316
317 _PUBLIC_ struct socket_address *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
318 {
319         if (!sock->ops->fn_get_my_addr) {
320                 return NULL;
321         }
322
323         return sock->ops->fn_get_my_addr(sock, mem_ctx);
324 }
325
326 _PUBLIC_ int socket_get_fd(struct socket_context *sock)
327 {
328         if (!sock->ops->fn_get_fd) {
329                 return -1;
330         }
331
332         return sock->ops->fn_get_fd(sock);
333 }
334
335 /*
336   call dup() on a socket, and close the old fd. This is used to change
337   the fd to the lowest available number, to make select() more
338   efficient (select speed depends on the maxiumum fd number passed to
339   it)
340 */
341 _PUBLIC_ NTSTATUS socket_dup(struct socket_context *sock)
342 {
343         int fd;
344         if (sock->fd == -1) {
345                 return NT_STATUS_INVALID_HANDLE;
346         }
347         fd = dup(sock->fd);
348         if (fd == -1) {
349                 return map_nt_error_from_unix(errno);
350         }
351         close(sock->fd);
352         sock->fd = fd;
353         return NT_STATUS_OK;
354         
355 }
356
357 /* Create a new socket_address.  The type must match the socket type.
358  * The host parameter may be an IP or a hostname 
359  */
360
361 _PUBLIC_ struct socket_address *socket_address_from_strings(TALLOC_CTX *mem_ctx,
362                                                             const char *family,
363                                                             const char *host,
364                                                             int port)
365 {
366         struct socket_address *addr = talloc(mem_ctx, struct socket_address);
367         if (!addr) {
368                 return NULL;
369         }
370
371         addr->family = family;
372         addr->addr = talloc_strdup(addr, host);
373         if (!addr->addr) {
374                 talloc_free(addr);
375                 return NULL;
376         }
377         addr->port = port;
378         addr->sockaddr = NULL;
379         addr->sockaddrlen = 0;
380
381         return addr;
382 }
383
384 /* Create a new socket_address.  Copy the struct sockaddr into the new
385  * structure.  Used for hooks in the kerberos libraries, where they
386  * supply only a struct sockaddr */
387
388 _PUBLIC_ struct socket_address *socket_address_from_sockaddr(TALLOC_CTX *mem_ctx, 
389                                                              struct sockaddr *sockaddr, 
390                                                              size_t sockaddrlen)
391 {
392         struct socket_address *addr = talloc(mem_ctx, struct socket_address);
393         if (!addr) {
394                 return NULL;
395         }
396         addr->family = NULL; 
397         addr->addr = NULL;
398         addr->port = 0;
399         addr->sockaddr = talloc_memdup(addr, sockaddr, sockaddrlen);
400         if (!addr->sockaddr) {
401                 talloc_free(addr);
402                 return NULL;
403         }
404         addr->sockaddrlen = sockaddrlen;
405         return addr;
406 }
407
408 _PUBLIC_ const struct socket_ops *socket_getops_byname(const char *family, enum socket_type type)
409 {
410         extern const struct socket_ops *socket_ipv4_ops(enum socket_type);
411         extern const struct socket_ops *socket_ipv6_ops(enum socket_type);
412         extern const struct socket_ops *socket_unixdom_ops(enum socket_type);
413
414         if (strcmp("ip", family) == 0 || 
415             strcmp("ipv4", family) == 0) {
416                 return socket_ipv4_ops(type);
417         }
418
419 #if HAVE_SOCKET_IPV6
420         if (strcmp("ipv6", family) == 0) {
421                 if (lp_parm_bool(-1, "socket", "noipv6", False)) {
422                         DEBUG(3, ("IPv6 support was disabled in smb.conf"));
423                         return NULL;
424                 }
425                 return socket_ipv6_ops(type);
426         }
427 #endif
428
429         if (strcmp("unix", family) == 0) {
430                 return socket_unixdom_ops(type);
431         }
432
433         return NULL;
434 }
435
436 enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
437
438 static const struct {
439         const char *name;
440         int level;
441         int option;
442         int value;
443         int opttype;
444 } socket_options[] = {
445   {"SO_KEEPALIVE",      SOL_SOCKET,    SO_KEEPALIVE,    0,                 OPT_BOOL},
446   {"SO_REUSEADDR",      SOL_SOCKET,    SO_REUSEADDR,    0,                 OPT_BOOL},
447   {"SO_BROADCAST",      SOL_SOCKET,    SO_BROADCAST,    0,                 OPT_BOOL},
448 #ifdef TCP_NODELAY
449   {"TCP_NODELAY",       IPPROTO_TCP,   TCP_NODELAY,     0,                 OPT_BOOL},
450 #endif
451 #ifdef IPTOS_LOWDELAY
452   {"IPTOS_LOWDELAY",    IPPROTO_IP,    IP_TOS,          IPTOS_LOWDELAY,    OPT_ON},
453 #endif
454 #ifdef IPTOS_THROUGHPUT
455   {"IPTOS_THROUGHPUT",  IPPROTO_IP,    IP_TOS,          IPTOS_THROUGHPUT,  OPT_ON},
456 #endif
457 #ifdef SO_REUSEPORT
458   {"SO_REUSEPORT",      SOL_SOCKET,    SO_REUSEPORT,    0,                 OPT_BOOL},
459 #endif
460 #ifdef SO_SNDBUF
461   {"SO_SNDBUF",         SOL_SOCKET,    SO_SNDBUF,       0,                 OPT_INT},
462 #endif
463 #ifdef SO_RCVBUF
464   {"SO_RCVBUF",         SOL_SOCKET,    SO_RCVBUF,       0,                 OPT_INT},
465 #endif
466 #ifdef SO_SNDLOWAT
467   {"SO_SNDLOWAT",       SOL_SOCKET,    SO_SNDLOWAT,     0,                 OPT_INT},
468 #endif
469 #ifdef SO_RCVLOWAT
470   {"SO_RCVLOWAT",       SOL_SOCKET,    SO_RCVLOWAT,     0,                 OPT_INT},
471 #endif
472 #ifdef SO_SNDTIMEO
473   {"SO_SNDTIMEO",       SOL_SOCKET,    SO_SNDTIMEO,     0,                 OPT_INT},
474 #endif
475 #ifdef SO_RCVTIMEO
476   {"SO_RCVTIMEO",       SOL_SOCKET,    SO_RCVTIMEO,     0,                 OPT_INT},
477 #endif
478   {NULL,0,0,0,0}};
479
480
481 /**
482  Set user socket options.
483 **/
484 _PUBLIC_ void set_socket_options(int fd, const char *options)
485 {
486         const char **options_list = str_list_make(NULL, options, " \t,");
487         int j;
488
489         if (!options_list)
490                 return;
491
492         for (j = 0; options_list[j]; j++) {
493                 const char *tok = options_list[j];
494                 int ret=0,i;
495                 int value = 1;
496                 char *p;
497                 BOOL got_value = False;
498
499                 if ((p = strchr(tok,'='))) {
500                         *p = 0;
501                         value = atoi(p+1);
502                         got_value = True;
503                 }
504
505                 for (i=0;socket_options[i].name;i++)
506                         if (strequal(socket_options[i].name,tok))
507                                 break;
508
509                 if (!socket_options[i].name) {
510                         DEBUG(0,("Unknown socket option %s\n",tok));
511                         continue;
512                 }
513
514                 switch (socket_options[i].opttype) {
515                 case OPT_BOOL:
516                 case OPT_INT:
517                         ret = setsockopt(fd,socket_options[i].level,
518                                                 socket_options[i].option,(char *)&value,sizeof(int));
519                         break;
520
521                 case OPT_ON:
522                         if (got_value)
523                                 DEBUG(0,("syntax error - %s does not take a value\n",tok));
524
525                         {
526                                 int on = socket_options[i].value;
527                                 ret = setsockopt(fd,socket_options[i].level,
528                                                         socket_options[i].option,(char *)&on,sizeof(int));
529                         }
530                         break;    
531                 }
532       
533                 if (ret != 0)
534                         DEBUG(0,("Failed to set socket option %s (Error %s)\n",tok, strerror(errno) ));
535         }
536
537         talloc_free(options_list);
538 }
539