1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * This file delivers /usr/lib/servinfo which provides description for
29 * IANA and running RPC services. Given a IANA name or RPC program name
30 * or number, the program uses getservbyname(3SOCKET) and rpcbind(3NSL)
31 * to obtain port and proto information for the specified service.
32 */
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <strings.h>
37 #include <netconfig.h>
38 #include <netdb.h>
39 #include <rpc/rpc.h>
40 #include <rpc/rpcent.h>
41 #include <sys/types.h>
42 #include <netinet/in.h>
43 #include <netdir.h>
44 #include <inttypes.h>
45 #include <limits.h>
46 #include <libintl.h>
47 #include <locale.h>
48
49 #ifndef TEXT_DOMAIN
50 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
51 #endif /* TEXT_DOMAIN */
52
53 #define TCP "tcp"
54 #define TCP6 "tcp6"
55 #define UDP "udp"
56 #define UDP6 "udp6"
57
58 #define DEFAULT 0x1
59 #define PORT 0x2
60 #define PROTO 0x4
61
62 #define NETID_LEN 12 /* length for a netid or 2^16 port value */
63
64 static void
usage(char * arg0)65 usage(char *arg0)
66 {
67 (void) fprintf(stderr, gettext("Usage: %s [-R] [-Pp] [-tu[6]] "
68 "-s service_name\n"), arg0);
69 }
70
71 static rpcport_t
uaddr2port(char * addr)72 uaddr2port(char *addr)
73 {
74 rpcport_t port = 0;
75 char *dot, *p;
76
77 if ((dot = strrchr(addr, '.')) == 0) {
78 return (0);
79 } else {
80 if (dot == addr)
81 return (0);
82
83 p = dot - 1;
84 while (*p != '.') {
85 /*
86 * If the first dot hasn't been seen, it's a
87 * malformed universal address.
88 */
89 if (p == addr)
90 return (0);
91 p--;
92 }
93
94 port = strtol(p + 1, &dot, 10) << 8;
95 port = port | strtol(dot + 1, (char **)NULL, 10);
96 }
97
98 return (port);
99 }
100
101 static int
svc_getrpcinfo(char * sname,char * sproto,int options)102 svc_getrpcinfo(char *sname, char *sproto, int options)
103 {
104 struct netconfig *nconf;
105 struct rpcblist *blist;
106 int prognum = -1;
107 rpcport_t rpc_port;
108 struct rpcent rentry;
109 struct rpcent *rpc;
110 char line[LINE_MAX] = "";
111 int line_len = LINE_MAX - 1;
112 char buf[NETID_LEN];
113
114 prognum = atoi(sname);
115 if (prognum > 0)
116 rpc = (struct rpcent *)getrpcbynumber(prognum);
117 else
118 rpc = (struct rpcent *)getrpcbyname(sname);
119
120 /*
121 * If an entry doesn't exist, it could be a running program
122 * without a registered RPC entry.
123 */
124 if (rpc == NULL) {
125 if (prognum <= 0) {
126 (void) fprintf(stderr,
127 gettext("Can't get rpc entry\n"));
128 return (1);
129 }
130
131 rpc = &rentry;
132 rpc->r_number = prognum;
133 rpc->r_name = sname;
134 }
135
136 if (setnetconfig() == NULL) {
137 (void) fprintf(stderr, gettext("setnetconfig failed\n"));
138 return (1);
139 }
140
141 if ((nconf = getnetconfigent(TCP)) == NULL) {
142 (void) fprintf(stderr, gettext("getnetconfig failed\n"));
143 return (1);
144 }
145
146 if ((blist = (struct rpcblist *)rpcb_getmaps(nconf, "localhost"))
147 == NULL) {
148 (void) fprintf(stderr,
149 gettext("Failed: rpcb_getmaps failed\n"));
150 return (1);
151 }
152
153 for (; blist != NULL; blist = blist->rpcb_next) {
154 if (blist->rpcb_map.r_prog != rpc->r_number)
155 continue;
156
157 if (sproto) {
158 if (strcmp(blist->rpcb_map.r_netid, sproto) != 0)
159 continue;
160 } else {
161 if (strcmp(blist->rpcb_map.r_netid, UDP) &&
162 strcmp(blist->rpcb_map.r_netid, UDP6) &&
163 strcmp(blist->rpcb_map.r_netid, TCP) &&
164 strcmp(blist->rpcb_map.r_netid, TCP6))
165 continue;
166 }
167 rpc_port = uaddr2port(blist->rpcb_map.r_addr);
168
169 if (options & DEFAULT) {
170 (void) printf("Program %ld\n", blist->rpcb_map.r_prog);
171 (void) printf("Protocol %s\n", blist->rpcb_map.r_netid);
172 (void) printf("Port %ld\n", rpc_port);
173 (void) printf("Version %ld\n", blist->rpcb_map.r_vers);
174 (void) printf("Name %s\n", rpc->r_name);
175
176 } else if (options & PROTO) {
177 if (strstr(line, blist->rpcb_map.r_netid))
178 continue;
179
180 (void) snprintf(buf, sizeof (buf), "%5s ",
181 blist->rpcb_map.r_netid);
182
183 if (strlen(buf) > line_len)
184 continue;
185
186 line_len = line_len - strlen(buf);
187 (void) strlcat(line, buf, sizeof (line));
188 } else {
189 (void) snprintf(buf, sizeof (buf), "%-7ld ", rpc_port);
190
191 if (strstr(line, buf) || strlen(buf) > line_len)
192 continue;
193
194 line_len = line_len - strlen(buf);
195 (void) strlcat(line, buf, sizeof (line));
196 }
197 }
198
199 /*
200 * Print the concatenated output if options is PROTO or PORT.
201 */
202 if (options & (PROTO | PORT))
203 (void) puts(line);
204
205 return (0);
206 }
207
208 int
main(int argc,char * argv[])209 main(int argc, char *argv[])
210 {
211 struct servent *service;
212 char *sname = NULL;
213 char *sproto = NULL;
214 int options = DEFAULT;
215 int c, isrpc = 0, v6_flag = 0;
216
217 (void) setlocale(LC_ALL, "");
218 (void) textdomain(TEXT_DOMAIN);
219
220 optind = 1;
221 opterr = 1;
222 while ((c = getopt(argc, argv, "s:PplRtu6?")) != -1) {
223 switch (c) {
224 case 's':
225 sname = optarg;
226 break;
227 case 't':
228 sproto = TCP;
229 break;
230 case 'u':
231 sproto = UDP;
232 break;
233 case '6':
234 v6_flag = 1;
235 break;
236 case 'P':
237 options = PROTO;
238 break;
239 case 'p':
240 options = PORT;
241 break;
242 case 'R':
243 isrpc = 1;
244 break;
245 default:
246 usage(argv[0]);
247 return (1);
248 }
249 }
250 if (sname == NULL) {
251 usage(argv[0]);
252 return (1);
253 }
254
255 /*
256 * Specified service is an RPC service.
257 */
258 if (isrpc) {
259 if (sproto && v6_flag) {
260 if (strcmp(sproto, TCP) == 0)
261 sproto = TCP6;
262 if (strcmp(sproto, UDP) == 0)
263 sproto = UDP6;
264 }
265
266 return (svc_getrpcinfo(sname, sproto, options));
267 }
268
269 if ((service = getservbyname(sname, sproto)) == NULL) {
270 (void) fprintf(stderr, gettext(
271 "Failed to get information for %s\n"), sname);
272 return (1);
273 }
274
275 if (options & DEFAULT) {
276 (void) printf("Name %s\n", service->s_name);
277 (void) printf("Protocol %s\n", service->s_proto);
278 (void) printf("Port %d\n", htons(service->s_port));
279 } else if (options & PROTO)
280 (void) printf("%s\n", service->s_proto);
281 else
282 (void) printf("%d\n", htons(service->s_port));
283
284 return (0);
285 }
286