xref: /illumos-gate/usr/src/cmd/svc/servinfo/servinfo.c (revision edb348833aaacfa1176e502ad38875fd0b2717ab)
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
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
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
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
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