xref: /haiku/src/bin/network/netstat/netstat.cpp (revision a2efc1d6e0db60ae3c6ae29f04012c2d96f59347)
1 /*
2  * Copyright 2006-2019, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  *		James Woodcock
8  */
9 
10 
11 #include <arpa/inet.h>
12 #include <errno.h>
13 #include <getopt.h>
14 #include <net/if.h>
15 #include <netdb.h>
16 #include <netinet/in.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 
22 #include <SupportDefs.h>
23 
24 #include <net_stat.h>
25 #include <syscalls.h>
26 
27 
28 extern const char* __progname;
29 const char* kProgramName = __progname;
30 
31 static int sResolveNames = 1;
32 
33 struct address_family {
34 	int			family;
35 	const char*	name;
36 	const char*	identifiers[4];
37 	void		(*print_address)(sockaddr* address);
38 };
39 
40 enum filter_flags {
41 	FILTER_FAMILY_MASK		= 0x0000ff,
42 	FILTER_PROTOCOL_MASK	= 0x00ff00,
43 	FILTER_STATE_MASK		= 0xff0000,
44 
45 	// Families
46 	FILTER_AF_INET			= 0x000001,
47 	FILTER_AF_INET6			= 0x000002,
48 	FILTER_AF_UNIX			= 0x000004,
49 
50 	// Protocols
51 	FILTER_IPPROTO_TCP		= 0x000100,
52 	FILTER_IPPROTO_UDP		= 0x000200,
53 
54 	// States
55 	FILTER_STATE_LISTEN		= 0x010000,
56 };
57 
58 // AF_INET family
59 static void inet_print_address(sockaddr* address);
60 
61 static const address_family kFamilies[] = {
62 	{
63 		AF_INET,
64 		"inet",
65 		{"AF_INET", "inet", "ipv4", NULL},
66 		inet_print_address
67 	},
68 	{ -1, NULL, {NULL}, NULL }
69 };
70 
71 
72 static void
inet_print_address(sockaddr * _address)73 inet_print_address(sockaddr* _address)
74 {
75 	sockaddr_in& address = *(sockaddr_in *)_address;
76 
77 	if (address.sin_family != AF_INET || address.sin_len == 0) {
78 		printf("%-22s", "-");
79 		return;
80 	}
81 
82 	hostent* host = NULL;
83 	servent* service = NULL;
84 	if (sResolveNames) {
85 		host = gethostbyaddr((const char*)&address.sin_addr, sizeof(in_addr),
86 			AF_INET);
87 		service = getservbyport(address.sin_port, NULL);
88 	}
89 
90 	const char *hostName;
91 	if (host != NULL)
92 		hostName = host->h_name;
93 	else if (address.sin_addr.s_addr == INADDR_ANY)
94 		hostName = "*";
95 	else
96 		hostName = inet_ntoa(address.sin_addr);
97 
98 	char buffer[128];
99 	int length = strlcpy(buffer, hostName, sizeof(buffer));
100 
101 	char port[64];
102 	if (service != NULL)
103 		strlcpy(port, service->s_name, sizeof(port));
104 	else if (address.sin_port == 0)
105 		strcpy(port, "*");
106 	else
107 		snprintf(port, sizeof(port), "%u", ntohs(address.sin_port));
108 
109 	snprintf(buffer + length, sizeof(buffer) - length, ":%s", port);
110 
111 	printf("%-22s", buffer);
112 }
113 
114 
115 //	#pragma mark -
116 
117 
118 void
usage(int status)119 usage(int status)
120 {
121 	printf("Usage: %s [-nh]\n", kProgramName);
122 	printf("Options:\n");
123 	printf("	-n	don't resolve names\n");
124 	printf("	-h	this help\n");
125 	printf("Filter options:\n");
126 	printf("	-4	IPv4\n");
127 	printf("	-6	IPv6\n");
128 	printf("	-x	Unix\n");
129 	printf("	-t	TCP\n");
130 	printf("	-u	UDP\n");
131 	printf("	-l	listen state\n");
132 
133 	exit(status);
134 }
135 
136 
137 bool
get_address_family(const char * argument,int32 & familyIndex)138 get_address_family(const char* argument, int32& familyIndex)
139 {
140 	for (int32 i = 0; kFamilies[i].family >= 0; i++) {
141 		for (int32 j = 0; kFamilies[i].identifiers[j]; j++) {
142 			if (!strcmp(argument, kFamilies[i].identifiers[j])) {
143 				// found a match
144 				familyIndex = i;
145 				return true;
146 			}
147 		}
148 	}
149 
150 	// defaults to AF_INET
151 	familyIndex = 0;
152 	return false;
153 }
154 
155 
156 //	#pragma mark -
157 
158 
159 int
main(int argc,char ** argv)160 main(int argc, char** argv)
161 {
162 	int optionIndex = 0;
163 	int opt;
164 	int filter = 0;
165 
166 	const static struct option kLongOptions[] = {
167 		{"help", no_argument, 0, 'h'},
168 		{"numeric", no_argument, 0, 'n'},
169 
170 		{"inet", no_argument, 0, '4'},
171 		{"inet6", no_argument, 0, '6'},
172 		{"unix", no_argument, 0, 'x'},
173 
174 		{"tcp", no_argument, 0, 't'},
175 		{"udp", no_argument, 0, 'u'},
176 
177 		{"listen", no_argument, 0, 'l'},
178 
179 		{0, 0, 0, 0}
180 	};
181 
182 	do {
183 		opt = getopt_long(argc, argv, "hn46xtul", kLongOptions,
184 			&optionIndex);
185 		switch (opt) {
186 			case -1:
187 				// end of arguments, do nothing
188 				break;
189 
190 			case 'n':
191 				sResolveNames = 0;
192 				break;
193 
194 			// Family filter
195 			case '4':
196 				filter |= FILTER_AF_INET;
197 				break;
198 			case '6':
199 				filter |= FILTER_AF_INET6;
200 				break;
201 			case 'x':
202 				filter |= FILTER_AF_UNIX;
203 				break;
204 			// Protocol filter
205 			case 't':
206 				filter |= FILTER_IPPROTO_TCP;
207 				break;
208 			case 'u':
209 				filter |= FILTER_IPPROTO_UDP;
210 				break;
211 			// State filter
212 			case 'l':
213 				filter |= FILTER_STATE_LISTEN;
214 				break;
215 
216 			case 'h':
217 			default:
218 				usage(0);
219 				break;
220 		}
221 	} while (opt != -1);
222 
223 	bool printProgram = true;
224 		// TODO: add some more program options... :-)
225 
226 	printf("Proto  Recv-Q Send-Q Local Address         Foreign Address       "
227 		"State        Program\n");
228 
229 	uint32 cookie = 0;
230 	int family = -1;
231 	net_stat stat;
232 	while (_kern_get_next_socket_stat(family, &cookie, &stat) == B_OK) {
233 		// Filter families
234 		if ((filter & FILTER_FAMILY_MASK) != 0) {
235 			if (((filter & FILTER_AF_INET) == 0 || family != AF_INET)
236 				&& ((filter & FILTER_AF_INET6) == 0 || family != AF_INET6)
237 				&& ((filter & FILTER_AF_UNIX) == 0 || family != AF_UNIX))
238 				continue;
239 		}
240 		// Filter protocols
241 		if ((filter & FILTER_PROTOCOL_MASK) != 0) {
242 			if (((filter & FILTER_IPPROTO_TCP) == 0
243 					|| stat.protocol != IPPROTO_TCP)
244 				&& ((filter & FILTER_IPPROTO_UDP) == 0
245 					|| stat.protocol != IPPROTO_UDP))
246 				continue;
247 		}
248 		if ((filter & FILTER_STATE_MASK) != 0) {
249 			if ((filter & FILTER_STATE_LISTEN) == 0
250 				|| strcmp(stat.state, "listen") != 0)
251 				continue;
252 		}
253 
254 		protoent* proto = getprotobynumber(stat.protocol);
255 		if (proto != NULL)
256 			printf("%-6s ", proto->p_name);
257 		else
258 			printf("%-6d ", stat.protocol);
259 
260 		printf("%6lu ", stat.receive_queue_size);
261 		printf("%6lu ", stat.send_queue_size);
262 
263 		inet_print_address((sockaddr*)&stat.address);
264 		inet_print_address((sockaddr*)&stat.peer);
265 		printf("%-12s ", stat.state);
266 
267 		team_info info;
268 		if (printProgram && get_team_info(stat.owner, &info) == B_OK) {
269 			// remove arguments
270 			char* name = strchr(info.args, ' ');
271 			if (name != NULL)
272 				name[0] = '\0';
273 
274 			// remove path name
275 			name = strrchr(info.args, '/');
276 			if (name != NULL)
277 				name++;
278 			else
279 				name = info.args;
280 
281 			printf("%" B_PRId32 "/%s\n", stat.owner, name);
282 		} else
283 			printf("%" B_PRId32 "\n", stat.owner);
284 	}
285 
286 	return 0;
287 }
288 
289