Make UDP sockets not blocking
[nfs-utils.git] / support / nfs / svc_socket.c
1 /* Copyright (C) 2002 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA.  */
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <netdb.h>
23 #include <rpc/rpc.h>
24 #include <sys/socket.h>
25 #include <sys/fcntl.h>
26 #include <errno.h>
27
28 #ifdef _LIBC
29 # include <libintl.h>
30 #else
31 # ifndef _
32 #  define _(s)                  (s)
33 # endif
34 # define __socket(d, t, p)      socket ((d), (t), (p))
35 # define __close(f)             close ((f))
36 #endif
37
38 static int
39 svc_socket (u_long number, int type, int protocol, int reuse)
40 {
41   struct sockaddr_in addr;
42   socklen_t len = sizeof (struct sockaddr_in);
43   char rpcdata [1024], servdata [1024];
44   struct rpcent rpcbuf, *rpcp;
45   struct servent servbuf, *servp = NULL;
46   int sock, ret;
47   const char *proto = protocol == IPPROTO_TCP ? "tcp" : "udp";
48
49   if ((sock = __socket (AF_INET, type, protocol)) < 0)
50     {
51       perror (_("svc_socket: socket creation problem"));
52       return sock;
53     }
54
55   if (reuse)
56     {
57       ret = 1;
58       ret = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &ret,
59                         sizeof (ret));
60       if (ret < 0)
61         {
62           perror (_("svc_socket: socket reuse problem"));
63           return ret;
64         }
65     }
66
67   memset (&addr, 0, sizeof (addr));
68   addr.sin_family = AF_INET;
69
70   ret = getrpcbynumber_r (number, &rpcbuf, rpcdata, sizeof rpcdata,
71                           &rpcp);
72   if (ret == 0 && rpcp != NULL)
73     {
74       /* First try name.  */
75       ret = getservbyname_r (rpcp->r_name, proto, &servbuf, servdata,
76                              sizeof servdata, &servp);
77       if ((ret != 0 || servp == NULL) && rpcp->r_aliases)
78         {
79           const char **a;
80
81           /* Then we try aliases.  */
82           for (a = (const char **) rpcp->r_aliases; *a != NULL; a++) 
83             {
84               ret = getservbyname_r (*a, proto, &servbuf, servdata,
85                                      sizeof servdata, &servp);
86               if (ret == 0 && servp != NULL)
87                 break;
88             }
89         }
90     }
91
92   if (ret == 0 && servp != NULL)
93     {
94       addr.sin_port = servp->s_port;
95       if (bind (sock, (struct sockaddr *) &addr, len) < 0)
96         {
97           perror (_("svc_socket: bind problem"));
98           (void) __close (sock);
99           sock = -1;
100         }
101     }
102   else
103     {
104       if (bindresvport (sock, &addr))
105         {
106           addr.sin_port = 0;
107           if (bind (sock, (struct sockaddr *) &addr, len) < 0)
108             {
109               perror (_("svc_socket: bind problem"));
110               (void) __close (sock);
111               sock = -1;
112             }
113         }
114     }
115
116   if (sock >= 0)
117     {
118             /* This socket might be shared among multiple processes
119              * if mountd is run multi-threaded.  So it is safest to
120              * make it non-blocking, else all threads might wake
121              * one will get the data, and the others will block
122              * indefinitely.
123              * In all cases, transaction on this socket are atomic
124              * (accept for TCP, packet-read and packet-write for UDP)
125              * so O_NONBLOCK will not confuse unprepared code causing
126              * it to corrupt messages.
127              * It generally safest to have O_NONBLOCK when doing an accept
128              * as if we get a RST after the SYN and before accept runs,
129              * we can block despite being told there was an acceptable
130              * connection.
131              */
132         int flags;
133         if ((flags = fcntl(sock, F_GETFL)) < 0)
134           {
135               perror (_("svc_socket: can't get socket flags"));
136               (void) __close (sock);
137               sock = -1;
138           }
139         else if (fcntl(sock, F_SETFL, flags|O_NONBLOCK) < 0)
140           {
141               perror (_("svc_socket: can't set socket flags"));
142               (void) __close (sock);
143               sock = -1;
144           }
145     }
146
147   return sock;
148 }
149
150 /*
151  * Create and bind a TCP socket based on program number
152  */
153 int
154 svctcp_socket (u_long number, int reuse)
155 {
156   return svc_socket (number, SOCK_STREAM, IPPROTO_TCP, reuse);
157 }
158
159 /*
160  * Create and bind a UDP socket based on program number
161  */
162 int
163 svcudp_socket (u_long number, int reuse)
164 {
165   return svc_socket (number, SOCK_DGRAM, IPPROTO_UDP, reuse);
166 }
167
168 #ifdef TEST
169 static int
170 check (u_long number, u_short port, int protocol, int reuse)
171 {
172   int socket;
173   int result;
174   struct sockaddr_in addr;
175   socklen_t len = sizeof (struct sockaddr_in);
176
177   if (protocol == IPPROTO_TCP)
178     socket = svctcp_socket (number, reuse);
179   else
180     socket = svcudp_socket (number, reuse);
181
182   if (socket < 0)
183     return 1;
184
185   result = getsockname (socket, (struct sockaddr *) &addr, &len);
186   if (result == 0)
187     {
188       if (port != 0 && ntohs (addr.sin_port) != port)
189         printf ("Program: %ld, expect port: %d, got: %d\n",
190                 number, port, ntohs (addr.sin_port)); 
191       else
192         printf ("Program: %ld, port: %d\n",
193                 number, ntohs (addr.sin_port)); 
194     }
195
196   close (socket);
197   return result;
198 }
199
200 int
201 main (void)
202 {
203   int result = 0;
204
205   result += check (100001, 0, IPPROTO_TCP, 0);
206   result += check (100001, 0, IPPROTO_UDP, 0);
207   result += check (100003, 2049, IPPROTO_TCP, 1);
208   result += check (100003, 2049, IPPROTO_UDP, 1);
209
210   return result;
211 }
212 #endif