rpc.mountd: let mountd consult /etc/services for port
[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 int getservport(u_long number, const char *proto)
39 {
40         char rpcdata[1024], servdata[1024];
41         struct rpcent rpcbuf, *rpcp;
42         struct servent servbuf, *servp = NULL;
43         int ret;
44
45         ret = getrpcbynumber_r(number, &rpcbuf, rpcdata, sizeof rpcdata,
46                                 &rpcp);
47         if (ret == 0 && rpcp != NULL) {
48                 /* First try name.  */
49                 ret = getservbyname_r(rpcp->r_name, proto, &servbuf, servdata,
50                                         sizeof servdata, &servp);
51                 if ((ret != 0 || servp == NULL) && rpcp->r_aliases) {
52                         const char **a;
53
54                         /* Then we try aliases.  */
55                         for (a = (const char **) rpcp->r_aliases; *a != NULL; a++) {
56                                 ret = getservbyname_r(*a, proto, &servbuf, servdata,
57                                                         sizeof servdata, &servp);
58                                 if (ret == 0 && servp != NULL)
59                                         break;
60                         }
61                 }
62         }
63
64         if (ret == 0 && servp != NULL)
65                 return ntohs(servp->s_port);
66
67         return 0;
68 }
69
70 static int
71 svc_socket (u_long number, int type, int protocol, int reuse)
72 {
73   struct sockaddr_in addr;
74   socklen_t len = sizeof (struct sockaddr_in);
75   int sock, ret;
76   const char *proto = protocol == IPPROTO_TCP ? "tcp" : "udp";
77
78   if ((sock = __socket (AF_INET, type, protocol)) < 0)
79     {
80       perror (_("svc_socket: socket creation problem"));
81       return sock;
82     }
83
84   if (reuse)
85     {
86       ret = 1;
87       ret = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &ret,
88                         sizeof (ret));
89       if (ret < 0)
90         {
91           perror (_("svc_socket: socket reuse problem"));
92           return ret;
93         }
94     }
95
96   memset (&addr, 0, sizeof (addr));
97   addr.sin_family = AF_INET;
98   addr.sin_port = htons(getservport(number, proto));
99
100   if (bind(sock, (struct sockaddr *) &addr, len) < 0)
101     {
102       perror (_("svc_socket: bind problem"));
103       (void) __close(sock);
104       sock = -1;
105     }
106
107   if (sock >= 0)
108     {
109             /* This socket might be shared among multiple processes
110              * if mountd is run multi-threaded.  So it is safest to
111              * make it non-blocking, else all threads might wake
112              * one will get the data, and the others will block
113              * indefinitely.
114              * In all cases, transaction on this socket are atomic
115              * (accept for TCP, packet-read and packet-write for UDP)
116              * so O_NONBLOCK will not confuse unprepared code causing
117              * it to corrupt messages.
118              * It generally safest to have O_NONBLOCK when doing an accept
119              * as if we get a RST after the SYN and before accept runs,
120              * we can block despite being told there was an acceptable
121              * connection.
122              */
123         int flags;
124         if ((flags = fcntl(sock, F_GETFL)) < 0)
125           {
126               perror (_("svc_socket: can't get socket flags"));
127               (void) __close (sock);
128               sock = -1;
129           }
130         else if (fcntl(sock, F_SETFL, flags|O_NONBLOCK) < 0)
131           {
132               perror (_("svc_socket: can't set socket flags"));
133               (void) __close (sock);
134               sock = -1;
135           }
136     }
137
138   return sock;
139 }
140
141 /*
142  * Create and bind a TCP socket based on program number
143  */
144 int
145 svctcp_socket (u_long number, int reuse)
146 {
147   return svc_socket (number, SOCK_STREAM, IPPROTO_TCP, reuse);
148 }
149
150 /*
151  * Create and bind a UDP socket based on program number
152  */
153 int
154 svcudp_socket (u_long number)
155 {
156   return svc_socket (number, SOCK_DGRAM, IPPROTO_UDP, FALSE);
157 }
158
159 #ifdef TEST
160 static int
161 check (u_long number, u_short port, int protocol, int reuse)
162 {
163   int socket;
164   int result;
165   struct sockaddr_in addr;
166   socklen_t len = sizeof (struct sockaddr_in);
167
168   if (protocol == IPPROTO_TCP)
169     socket = svctcp_socket (number, reuse);
170   else
171     socket = svcudp_socket (number);
172
173   if (socket < 0)
174     return 1;
175
176   result = getsockname (socket, (struct sockaddr *) &addr, &len);
177   if (result == 0)
178     {
179       if (port != 0 && ntohs (addr.sin_port) != port)
180         printf ("Program: %ld, expect port: %d, got: %d\n",
181                 number, port, ntohs (addr.sin_port)); 
182       else
183         printf ("Program: %ld, port: %d\n",
184                 number, ntohs (addr.sin_port)); 
185     }
186
187   close (socket);
188   return result;
189 }
190
191 int
192 main (void)
193 {
194   int result = 0;
195
196   result += check (100001, 0, IPPROTO_TCP, 0);
197   result += check (100001, 0, IPPROTO_UDP, 0);
198   result += check (100003, 2049, IPPROTO_TCP, 1);
199   result += check (100003, 2049, IPPROTO_UDP, 1);
200
201   return result;
202 }
203 #endif