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
28extern const char* __progname;
29const char* kProgramName = __progname;
30
31static int sResolveNames = 1;
32
33struct address_family {
34	int			family;
35	const char*	name;
36	const char*	identifiers[4];
37	void		(*print_address)(sockaddr* address);
38};
39
40enum 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
59static void inet_print_address(sockaddr* address);
60
61static 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
72static void
73inet_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
118void
119usage(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
137bool
138get_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
159int
160main(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