1/*
2 * Copyright 2006-2010, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Oliver Tappe, zooey@hirschkaefer.de
7 *		Axel D��rfler, axeld@pinc-software.de
8 */
9
10
11#include <ctype.h>
12#include <errno.h>
13#include <net/if_dl.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <sys/socket.h>
18
19#include <NetworkInterface.h>
20
21
22extern const char* __progname;
23
24
25static const size_t kBufferSize = 1500;
26static const uint32 kFrameType = 0x8998;
27
28static const char* kProgramName = __progname;
29
30
31static void
32parse_mac_address(const char* string, uint8* mac)
33{
34	if (sscanf(string, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", &mac[0],
35			&mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) < 6) {
36		fprintf(stderr, "%s: Invalid MAC address.\n", kProgramName);
37		exit(EXIT_FAILURE);
38	}
39}
40
41
42static void
43link_client(int fd, const BNetworkAddress& server)
44{
45	char buffer[kBufferSize];
46
47	while (fgets(buffer, kBufferSize, stdin) != NULL) {
48		size_t length = strlen(buffer);
49		if (length > 0)
50			length--;
51
52		if (sendto(fd, buffer, length, 0, server, server.Length()) < 0) {
53			fprintf(stderr, "%s: sendto(): %s\n", kProgramName,
54				strerror(errno));
55			exit(EXIT_FAILURE);
56		}
57		printf("sent %" B_PRIuSIZE " bytes...\n", length);
58
59		ssize_t bytesRead = recvfrom(fd, buffer, kBufferSize - 1, 0, NULL,
60			NULL);
61		if (bytesRead < 0) {
62			fprintf(stderr, "%s: recvfrom(): %s\n", kProgramName,
63				strerror(errno));
64			exit(EXIT_FAILURE);
65		}
66		buffer[bytesRead] = 0;
67		printf("-> %s\n", buffer);
68	}
69}
70
71
72static void
73link_server(int fd)
74{
75	while (true) {
76		BNetworkAddress client;
77		socklen_t length = sizeof(client);
78
79		char buffer[kBufferSize];
80		ssize_t bytesRead = recvfrom(fd, buffer, kBufferSize - 1, 0, client,
81			&length);
82		if (bytesRead < 0) {
83			fprintf(stderr, "%s: recvfrom(): %s\n", kProgramName,
84				strerror(errno));
85			exit(EXIT_FAILURE);
86		}
87		buffer[bytesRead] = '\0';
88
89		printf("got <%s> from client %s\n", buffer, client.ToString().String());
90
91		for (int i = 0; i < bytesRead; i++) {
92			if (islower(buffer[i]))
93				buffer[i] = toupper(buffer[i]);
94			else if (isupper(buffer[i]))
95				buffer[i] = tolower(buffer[i]);
96		}
97		printf("replying <%s>\n", buffer);
98
99		if (sendto(fd, buffer, bytesRead, 0, client, client.Length()) < 0) {
100			fprintf(stderr, "%s: sendto(): %s\n", kProgramName,
101				strerror(errno));
102			exit(EXIT_FAILURE);
103		}
104	}
105}
106
107
108int
109main(int argc, char** argv)
110{
111	enum {
112		CLIENT_MODE,
113		SERVER_MODE,
114		BROADCAST_MODE,
115	} mode = CLIENT_MODE;
116
117	if (argc < 3) {
118		fprintf(stderr, "usage: %s <device> client <server-mac-address>\n"
119			"or     %s <device> broadcast\n"
120			"or     %s <device> server\n", kProgramName, kProgramName,
121			kProgramName);
122		exit(EXIT_FAILURE);
123	}
124
125	BNetworkInterface interface(argv[1]);
126	BNetworkAddress link;
127	if (interface.GetHardwareAddress(link) != B_OK)
128		perror("get hardware address");
129
130	BNetworkAddress server;
131
132	if (!strcmp(argv[2], "client")) {
133		mode = CLIENT_MODE;
134		if (argc < 4) {
135			fprintf(stderr, "usage: %s client <server-mac-address>\n",
136				kProgramName);
137			exit(EXIT_FAILURE);
138		}
139
140		uint8 macAddress[6];
141		parse_mac_address(argv[3], macAddress);
142		server.SetToLinkLevel(macAddress, sizeof(macAddress));
143	} else if (!strcmp(argv[2], "broadcast")) {
144		mode = BROADCAST_MODE;
145
146		uint8 broadcastAddress[6];
147		for (size_t i = 0; i < sizeof(broadcastAddress); i++)
148			broadcastAddress[i] = 0xff;
149		server.SetToLinkLevel(broadcastAddress, sizeof(broadcastAddress));
150	} else if (!strcmp(argv[2], "server"))
151		mode = SERVER_MODE;
152
153	int fd = socket(AF_LINK, SOCK_DGRAM, 0);
154	if (fd < 0)
155		perror("socket");
156
157	// bind to protocol
158	link.SetLinkLevelFrameType(kFrameType);
159	server.SetLinkLevelFrameType(kFrameType);
160
161	if (bind(fd, link, link.Length()) != 0)
162		perror("bind");
163
164	socklen_t length = sizeof(link);
165	if (getsockname(fd, link, &length) != 0)
166		perror("getsockname");
167
168	printf("bound to %s\n", link.ToString().String());
169
170	if (mode == BROADCAST_MODE) {
171		int option = 1;
172		setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &option, sizeof(option));
173	}
174
175	switch (mode) {
176		case CLIENT_MODE:
177		case BROADCAST_MODE:
178			link_client(fd, server);
179			break;
180		case SERVER_MODE:
181			link_server(fd);
182			break;
183	}
184
185	return 0;
186}
187