]>
Commit | Line | Data |
---|---|---|
1 | autofs-5.0.8 - fix ipv6 libtirpc getport | |
2 | ||
3 | From: Ian Kent <ikent@redhat.com> | |
4 | ||
5 | The method that was being used to obtain a service port number | |
6 | when using libtirpc was wrong. | |
7 | --- | |
8 | CHANGELOG | 1 | |
9 | lib/rpc_subs.c | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- | |
10 | 2 files changed, 267 insertions(+), 17 deletions(-) | |
11 | ||
12 | diff --git a/CHANGELOG b/CHANGELOG | |
13 | index 68db340..9c87373 100644 | |
14 | --- a/CHANGELOG | |
15 | +++ b/CHANGELOG | |
16 | @@ -5,6 +5,7 @@ | |
17 | - fix task manager not getting signaled. | |
18 | - allow --with-systemd to take a path arg. | |
19 | - fix WITH_LIBTIRPC function name. | |
20 | +- fix ipv6 libtirpc getport. | |
21 | ||
22 | 17/10/2013 autofs-5.0.8 | |
23 | ======================= | |
24 | diff --git a/lib/rpc_subs.c b/lib/rpc_subs.c | |
25 | index 46b3e8d..2365b6e 100644 | |
26 | --- a/lib/rpc_subs.c | |
27 | +++ b/lib/rpc_subs.c | |
28 | @@ -234,6 +234,28 @@ static int rpc_do_create_client(struct sockaddr *addr, struct conn_info *info, i | |
29 | ||
30 | return 0; | |
31 | } | |
32 | +static int rpc_getport(struct conn_info *info, | |
33 | + struct pmap *parms, CLIENT *client) | |
34 | +{ | |
35 | + enum clnt_stat status; | |
36 | + | |
37 | + /* | |
38 | + * Check to see if server is up otherwise a getport will take | |
39 | + * forever to timeout. | |
40 | + */ | |
41 | + status = clnt_call(client, PMAPPROC_NULL, | |
42 | + (xdrproc_t) xdr_void, 0, (xdrproc_t) xdr_void, 0, | |
43 | + info->timeout); | |
44 | + | |
45 | + if (status == RPC_SUCCESS) { | |
46 | + status = clnt_call(client, PMAPPROC_GETPORT, | |
47 | + (xdrproc_t) xdr_pmap, (caddr_t) parms, | |
48 | + (xdrproc_t) xdr_u_short, (caddr_t) port, | |
49 | + info->timeout); | |
50 | + } | |
51 | + | |
52 | + return status; | |
53 | +} | |
54 | #else | |
55 | static int rpc_do_create_client(struct sockaddr *addr, struct conn_info *info, int *fd, CLIENT **client) | |
56 | { | |
57 | @@ -267,9 +289,6 @@ static int rpc_do_create_client(struct sockaddr *addr, struct conn_info *info, i | |
58 | laddr = (struct sockaddr *) &in4_laddr; | |
59 | in4_raddr->sin_port = htons(info->port); | |
60 | slen = sizeof(struct sockaddr_in); | |
61 | - /* Use rpcbind v2 for AF_INET */ | |
62 | - if (info->program == rpcb_prog) | |
63 | - info->version = PMAPVERS; | |
64 | } else if (addr->sa_family == AF_INET6) { | |
65 | struct sockaddr_in6 *in6_raddr = (struct sockaddr_in6 *) addr; | |
66 | in6_laddr.sin6_family = AF_INET6; | |
67 | @@ -324,6 +343,244 @@ static int rpc_do_create_client(struct sockaddr *addr, struct conn_info *info, i | |
68 | ||
69 | return 0; | |
70 | } | |
71 | + | |
72 | +/* | |
73 | + * Thankfully nfs-utils had already dealt with this. | |
74 | + * Thanks to Chuck Lever for his nfs-utils patch series, much of | |
75 | + * which is used here. | |
76 | + */ | |
77 | +static pthread_mutex_t proto_mutex = PTHREAD_MUTEX_INITIALIZER; | |
78 | + | |
79 | +static enum clnt_stat rpc_get_netid(const sa_family_t family, | |
80 | + const int protocol, char **netid) | |
81 | +{ | |
82 | + char *nc_protofmly, *nc_proto, *nc_netid; | |
83 | + struct netconfig *nconf; | |
84 | + struct protoent *proto; | |
85 | + void *handle; | |
86 | + | |
87 | + switch (family) { | |
88 | + case AF_LOCAL: | |
89 | + case AF_INET: | |
90 | + nc_protofmly = NC_INET; | |
91 | + break; | |
92 | + case AF_INET6: | |
93 | + nc_protofmly = NC_INET6; | |
94 | + break; | |
95 | + default: | |
96 | + return RPC_UNKNOWNPROTO; | |
97 | + } | |
98 | + | |
99 | + pthread_mutex_lock(&proto_mutex); | |
100 | + proto = getprotobynumber(protocol); | |
101 | + if (!proto) { | |
102 | + pthread_mutex_unlock(&proto_mutex); | |
103 | + return RPC_UNKNOWNPROTO; | |
104 | + } | |
105 | + nc_proto = strdup(proto->p_name); | |
106 | + pthread_mutex_unlock(&proto_mutex); | |
107 | + if (!nc_proto) | |
108 | + return RPC_SYSTEMERROR; | |
109 | + | |
110 | + handle = setnetconfig(); | |
111 | + while ((nconf = getnetconfig(handle)) != NULL) { | |
112 | + if (nconf->nc_protofmly != NULL && | |
113 | + strcmp(nconf->nc_protofmly, nc_protofmly) != 0) | |
114 | + continue; | |
115 | + if (nconf->nc_proto != NULL && | |
116 | + strcmp(nconf->nc_proto, nc_proto) != 0) | |
117 | + continue; | |
118 | + | |
119 | + nc_netid = strdup(nconf->nc_netid); | |
120 | + if (!nc_netid) { | |
121 | + free(nc_proto); | |
122 | + return RPC_SYSTEMERROR; | |
123 | + } | |
124 | + | |
125 | + *netid = nc_netid; | |
126 | + } | |
127 | + endnetconfig(handle); | |
128 | + free(nc_proto); | |
129 | + | |
130 | + return RPC_SUCCESS; | |
131 | +} | |
132 | + | |
133 | +static char *rpc_sockaddr2universal(const struct sockaddr *addr) | |
134 | +{ | |
135 | + const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *) addr; | |
136 | + const struct sockaddr_un *sun = (const struct sockaddr_un *) addr; | |
137 | + const struct sockaddr_in *sin = (const struct sockaddr_in *) addr; | |
138 | + char buf[INET6_ADDRSTRLEN + 8 /* for port information */]; | |
139 | + uint16_t port; | |
140 | + size_t count; | |
141 | + char *result; | |
142 | + int len; | |
143 | + | |
144 | + switch (addr->sa_family) { | |
145 | + case AF_LOCAL: | |
146 | + return strndup(sun->sun_path, sizeof(sun->sun_path)); | |
147 | + case AF_INET: | |
148 | + if (inet_ntop(AF_INET, (const void *)&sin->sin_addr.s_addr, | |
149 | + buf, (socklen_t)sizeof(buf)) == NULL) | |
150 | + goto out_err; | |
151 | + port = ntohs(sin->sin_port); | |
152 | + break; | |
153 | + case AF_INET6: | |
154 | + if (inet_ntop(AF_INET6, (const void *)&sin6->sin6_addr, | |
155 | + buf, (socklen_t)sizeof(buf)) == NULL) | |
156 | + goto out_err; | |
157 | + port = ntohs(sin6->sin6_port); | |
158 | + break; | |
159 | + default: | |
160 | + goto out_err; | |
161 | + } | |
162 | + | |
163 | + count = sizeof(buf) - strlen(buf); | |
164 | + len = snprintf(buf + strlen(buf), count, ".%u.%u", | |
165 | + (unsigned)(port >> 8), (unsigned)(port & 0xff)); | |
166 | + /* before glibc 2.0.6, snprintf(3) could return -1 */ | |
167 | + if (len < 0 || (size_t)len > count) | |
168 | + goto out_err; | |
169 | + | |
170 | + result = strdup(buf); | |
171 | + return result; | |
172 | + | |
173 | +out_err: | |
174 | + return NULL; | |
175 | +} | |
176 | + | |
177 | +static int rpc_universal2port(const char *uaddr) | |
178 | +{ | |
179 | + char *addrstr; | |
180 | + char *p, *endptr; | |
181 | + unsigned long portlo, porthi; | |
182 | + int port = -1; | |
183 | + | |
184 | + addrstr = strdup(uaddr); | |
185 | + if (!addrstr) | |
186 | + return -1; | |
187 | + | |
188 | + p = strrchr(addrstr, '.'); | |
189 | + if (!p) | |
190 | + goto out; | |
191 | + | |
192 | + portlo = strtoul(p + 1, &endptr, 10); | |
193 | + if (*endptr != '\0' || portlo > 255) | |
194 | + goto out; | |
195 | + *p = '\0'; | |
196 | + | |
197 | + p = strrchr(addrstr, '.'); | |
198 | + if (!p) | |
199 | + goto out; | |
200 | + | |
201 | + porthi = strtoul(p + 1, &endptr, 10); | |
202 | + if (*endptr != '\0' || porthi > 255) | |
203 | + goto out; | |
204 | + *p = '\0'; | |
205 | + | |
206 | + port = (porthi << 8) | portlo; | |
207 | + | |
208 | +out: | |
209 | + free(addrstr); | |
210 | + return port; | |
211 | +} | |
212 | + | |
213 | +static enum clnt_stat rpc_rpcb_getport(CLIENT *client, | |
214 | + struct rpcb *parms, | |
215 | + struct timeval timeout, | |
216 | + unsigned short *port) | |
217 | +{ | |
218 | + rpcvers_t rpcb_version; | |
219 | + struct rpc_err rpcerr; | |
220 | + int s_port = 0; | |
221 | + | |
222 | + for (rpcb_version = RPCBVERS_4; | |
223 | + rpcb_version >= RPCBVERS_3; | |
224 | + rpcb_version--) { | |
225 | + enum clnt_stat status; | |
226 | + char *uaddr = NULL; | |
227 | + | |
228 | + CLNT_CONTROL(client, CLSET_VERS, (void *) &rpcb_version); | |
229 | + status = CLNT_CALL(client, (rpcproc_t) RPCBPROC_GETADDR, | |
230 | + (xdrproc_t) xdr_rpcb, (void *) parms, | |
231 | + (xdrproc_t) xdr_wrapstring, (void *) &uaddr, | |
232 | + timeout); | |
233 | + | |
234 | + switch (status) { | |
235 | + case RPC_SUCCESS: | |
236 | + if ((uaddr == NULL) || (uaddr[0] == '\0')) | |
237 | + return RPC_PROGNOTREGISTERED; | |
238 | + | |
239 | + s_port = rpc_universal2port(uaddr); | |
240 | + xdr_free((xdrproc_t) xdr_wrapstring, (char *) &uaddr); | |
241 | + if (s_port == -1) { | |
242 | + return RPC_N2AXLATEFAILURE; | |
243 | + } | |
244 | + *port = s_port; | |
245 | + return RPC_SUCCESS; | |
246 | + | |
247 | + case RPC_PROGVERSMISMATCH: | |
248 | + clnt_geterr(client, &rpcerr); | |
249 | + if (rpcerr.re_vers.low > RPCBVERS4) | |
250 | + return status; | |
251 | + continue; | |
252 | + case RPC_PROCUNAVAIL: | |
253 | + case RPC_PROGUNAVAIL: | |
254 | + continue; | |
255 | + default: | |
256 | + /* Most likely RPC_TIMEDOUT or RPC_CANTRECV */ | |
257 | + return status; | |
258 | + } | |
259 | + } | |
260 | + | |
261 | + if (s_port == 0) | |
262 | + return RPC_PROGNOTREGISTERED; | |
263 | + | |
264 | + return RPC_PROCUNAVAIL; | |
265 | +} | |
266 | + | |
267 | +static enum clnt_stat rpc_getport(struct conn_info *info, | |
268 | + struct pmap *parms, CLIENT *client, | |
269 | + unsigned short *port) | |
270 | +{ | |
271 | + enum clnt_stat status; | |
272 | + struct sockaddr *paddr, addr; | |
273 | + struct rpcb rpcb_parms; | |
274 | + char *netid, *raddr; | |
275 | + | |
276 | + if (info->addr) | |
277 | + paddr = info->addr; | |
278 | + else { | |
279 | + if (!clnt_control(client, CLGET_SERVER_ADDR, (char *) &addr)) | |
280 | + return RPC_UNKNOWNADDR; | |
281 | + paddr = &addr; | |
282 | + } | |
283 | + | |
284 | + netid = NULL; | |
285 | + status = rpc_get_netid(paddr->sa_family, info->proto, &netid); | |
286 | + if (status != RPC_SUCCESS) | |
287 | + return status; | |
288 | + | |
289 | + raddr = rpc_sockaddr2universal(paddr); | |
290 | + if (!raddr) { | |
291 | + free(netid); | |
292 | + return RPC_UNKNOWNADDR; | |
293 | + } | |
294 | + | |
295 | + memset(&rpcb_parms, 0, sizeof(rpcb_parms)); | |
296 | + rpcb_parms.r_prog = parms->pm_prog; | |
297 | + rpcb_parms.r_vers = parms->pm_vers; | |
298 | + rpcb_parms.r_netid = netid; | |
299 | + rpcb_parms.r_addr = raddr; | |
300 | + rpcb_parms.r_owner = ""; | |
301 | + | |
302 | + status = rpc_rpcb_getport(client, &rpcb_parms, info->timeout, port); | |
303 | + | |
304 | + free(netid); | |
305 | + free(raddr); | |
306 | + | |
307 | + return status; | |
308 | +} | |
309 | #endif | |
310 | ||
311 | #if defined(HAVE_GETRPCBYNAME) || defined(HAVE_GETSERVBYNAME) | |
312 | @@ -647,20 +904,7 @@ int rpc_portmap_getport(struct conn_info *info, | |
313 | return ret; | |
314 | } | |
315 | ||
316 | - /* | |
317 | - * Check to see if server is up otherwise a getport will take | |
318 | - * forever to timeout. | |
319 | - */ | |
320 | - status = clnt_call(client, PMAPPROC_NULL, | |
321 | - (xdrproc_t) xdr_void, 0, (xdrproc_t) xdr_void, 0, | |
322 | - pmap_info.timeout); | |
323 | - | |
324 | - if (status == RPC_SUCCESS) { | |
325 | - status = clnt_call(client, PMAPPROC_GETPORT, | |
326 | - (xdrproc_t) xdr_pmap, (caddr_t) parms, | |
327 | - (xdrproc_t) xdr_u_short, (caddr_t) port, | |
328 | - pmap_info.timeout); | |
329 | - } | |
330 | + status = rpc_getport(&pmap_info, parms, client, port); | |
331 | ||
332 | if (!info->client) { | |
333 | /* | |
334 | @@ -867,6 +1111,11 @@ static int rpc_get_exports_proto(struct conn_info *info, exports *exp) | |
335 | clnt_control(client, CLSET_RETRY_TIMEOUT, (char *) &info->timeout); | |
336 | ||
337 | client->cl_auth = authunix_create_default(); | |
338 | + if (client->cl_auth == NULL) { | |
339 | + error(LOGOPT_ANY, "auth create failed"); | |
340 | + clnt_destroy(client); | |
341 | + return 0; | |
342 | + } | |
343 | ||
344 | vers_entry = 0; | |
345 | while (1) { |