support/nfs/svc_socket.c: New.
[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 <errno.h>
26
27 #ifdef _LIBC
28 # include <libintl.h>
29 #else
30 # include "config.h"
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 #if !defined HAVE_SVCTCP_SOCKET || !defined HAVE_SVCUDP_SOCKET
39 static int
40 svc_socket (u_long number, int type, int protocol, int reuse)
41 {
42   struct sockaddr_in addr;
43   socklen_t len = sizeof (struct sockaddr_in);
44   char rpcdata [1024], servdata [1024];
45   struct rpcent rpcbuf, *rpcp;
46   struct servent servbuf, *servp;
47   int sock, ret;
48   const char *proto = protocol == IPPROTO_TCP ? "tcp" : "udp";
49
50   if ((sock = __socket (AF_INET, type, protocol)) < 0)
51     {
52       perror (_("svc_socket: socket creation problem"));
53       return sock;
54     }
55
56   if (reuse)
57     {
58       ret = 1;
59       ret = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &ret,
60                         sizeof (ret));
61       if (ret < 0)
62         {
63           perror (_("svc_socket: socket reuse problem"));
64           return ret;
65         }
66     }
67
68   __bzero ((char *) &addr, sizeof (addr));
69   addr.sin_family = AF_INET;
70
71   ret = getrpcbynumber_r (number, &rpcbuf, rpcdata, sizeof rpcdata,
72                           &rpcp);
73   if (ret == 0 && rpcp != NULL)
74     {
75       /* First try name.  */
76       ret = getservbyname_r (rpcp->r_name, proto, &servbuf, servdata,
77                              sizeof servdata, &servp);
78       if ((ret != 0 || servp == NULL) && rpcp->r_aliases)
79         {
80           const char **a;
81
82           /* Then we try aliases.  */
83           for (a = (const char **) rpcp->r_aliases; *a != NULL; a++) 
84             {
85               ret = getservbyname_r (*a, proto, &servbuf, servdata,
86                                      sizeof servdata, &servp);
87               if (ret == 0 && servp != NULL)
88                 break;
89             }
90         }
91     }
92
93   if (ret == 0 && servp != NULL)
94     {
95       addr.sin_port = servp->s_port;
96       if (bind (sock, (struct sockaddr *) &addr, len) < 0)
97         {
98           perror (_("svc_socket: bind problem"));
99           (void) __close (sock);
100           sock = -1;
101         }
102     }
103   else
104     {
105       if (bindresvport (sock, &addr))
106         {
107           addr.sin_port = 0;
108           if (bind (sock, (struct sockaddr *) &addr, len) < 0)
109             {
110               perror (_("svc_socket: bind problem"));
111               (void) __close (sock);
112               sock = -1;
113             }
114         }
115     }
116
117   return sock;
118 }
119 #endif
120
121 #ifndef HAVE_SVCTCP_SOCKET
122 /*
123  * Create and bind a TCP socket based on program number
124  */
125 int
126 svctcp_socket (u_long number, int reuse)
127 {
128   return svc_socket (number, SOCK_STREAM, IPPROTO_TCP, reuse);
129 }
130 #endif
131
132 #ifndef HAVE_SVCUDP_SOCKET
133 /*
134  * Create and bind a UDP socket based on program number
135  */
136 int
137 svcudp_socket (u_long number, int reuse)
138 {
139   return svc_socket (number, SOCK_DGRAM, IPPROTO_UDP, reuse);
140 }
141 #endif
142
143 #ifdef TEST
144 static int
145 check (u_long number, u_short port, int protocol, int reuse)
146 {
147   int socket;
148   int result;
149   struct sockaddr_in addr;
150   socklen_t len = sizeof (struct sockaddr_in);
151
152   if (protocol == IPPROTO_TCP)
153     socket = svctcp_socket (number, reuse);
154   else
155     socket = svcudp_socket (number, reuse);
156
157   if (socket < 0)
158     return 1;
159
160   result = getsockname (socket, (struct sockaddr *) &addr, &len);
161   if (result == 0)
162     {
163       if (port != 0 && ntohs (addr.sin_port) != port)
164         printf ("Program: %ld, expect port: %d, got: %d\n",
165                 number, port, ntohs (addr.sin_port)); 
166       else
167         printf ("Program: %ld, port: %d\n",
168                 number, ntohs (addr.sin_port)); 
169     }
170
171   close (socket);
172   return result;
173 }
174
175 int
176 main (void)
177 {
178   int result = 0;
179
180   result += check (100001, 0, IPPROTO_TCP, 0);
181   result += check (100001, 0, IPPROTO_UDP, 0);
182   result += check (100003, 2049, IPPROTO_TCP, 1);
183   result += check (100003, 2049, IPPROTO_UDP, 1);
184
185   return result;
186 }
187 #endif