-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdirectoryServer5.c
More file actions
260 lines (236 loc) · 8.01 KB
/
Copy pathdirectoryServer5.c
File metadata and controls
260 lines (236 loc) · 8.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <stdlib.h>
#include <unistd.h>
#include "inet.h"
#include "common.h"
// Only active servers
// Created moreso for redundancy and easy scanning/querying
struct active_servers {
char name[50];
char addr[20];
int socket;
int to_remove;
LIST_ENTRY(active_servers) connections;
};
LIST_HEAD(servlist, active_servers);
// All active connections; both servers and clients
struct active_connections {
char addr[20];
int socket;
int to_remove;
int is_server;
struct active_servers *alias;
LIST_ENTRY(active_connections) connections;
};
LIST_HEAD(connlist, active_connections);
// Sends each server individually to avoid going over MAX
// Lets client know how many servers there are so it can handle all of the sent traffic
int client_query(struct servlist head, int serv_num, struct active_connections *client) {
int sock = client->socket;
if (serv_num > 0) {
char num[MAX];
snprintf(num, MAX, "%d", serv_num);
write(sock, num, MAX);
struct active_servers *server;
LIST_FOREACH(server, &head, connections) {
char out[MAX];
snprintf(out, MAX, "%s:%s:%d\n", server->name, server->addr, server->socket);
if (write(sock, out, MAX) < 1) {
return -1;
}
}
} else {
char out[MAX];
snprintf(out, MAX, "EIThere are no active chat rooms. Please try again later");
write(sock, out, MAX);
return 0;
}
return 1;
}
// Removes any connections and servers that are marked for removal
void remove_marked_connections(struct connlist connhead, struct servlist servhead, int *num_servs) {
// Removes from connections list
struct active_connections *conn_remove = LIST_FIRST(&connhead);
while (conn_remove != NULL) {
struct active_connections *next = LIST_NEXT(conn_remove, connections);
if (conn_remove->to_remove == 1) {
if (conn_remove->is_server == 1) {
conn_remove->alias->to_remove = 1; // Ensuring server gets deleted from server list
}
LIST_REMOVE(conn_remove, connections);
close(conn_remove->socket);
free(conn_remove);
}
conn_remove = next;
}
// Removes from server list
struct active_servers *serv_remove = LIST_FIRST(&servhead);
while (serv_remove != NULL) {
struct active_servers *next = LIST_NEXT(serv_remove, connections);
if (serv_remove->to_remove == 1) {
LIST_REMOVE(serv_remove, connections);
close(serv_remove->socket);
free(serv_remove);
(*num_servs)--;
}
serv_remove = next;
}
}
int main()
{
struct sockaddr_in cli_addr, serv_addr;
fd_set readset;
// makes heads for connection and server list
struct connlist conn_head;
struct servlist serv_head;
LIST_INIT(&conn_head);
LIST_INIT(&serv_head);
int num_of_servs = 0;
/* Create communication endpoint */
int sockfd; /* Listening socket */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("server: can't open stream socket");
return EXIT_FAILURE;
}
/* Add SO_REUSEADDRR option to prevent address in use errors (modified from: "Hands-On Network
* Programming with C" Van Winkle, 2019. https://learning.oreilly.com/library/view/hands-on-network-programming/9781789349863/5130fe1b-5c8c-42c0-8656-4990bb7baf2e.xhtml */
int true = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&true, sizeof(true)) < 0) {
perror("server: can't set stream socket address reuse option");
return EXIT_FAILURE;
}
/* Bind socket to local address */
memset((char *) &serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(SERV_TCP_PORT);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
perror("server: can't bind local address");
return EXIT_FAILURE;
}
listen(sockfd, 5);
for (;;) {
/* Initialize and populate your readset and compute maxfd */
FD_ZERO(&readset);
FD_SET(sockfd, &readset);
int max_fd = sockfd;
/* Look through current connections and figure out max_fd */
struct active_connections *conns;
LIST_FOREACH(conns, &conn_head, connections) {
int sock = conns->socket;
FD_SET(sock, &readset);
if (sock > max_fd) {
max_fd = sock;
}
}
/* FIXME: There should be a select call in here somewhere */
if (select(max_fd+1, &readset, NULL, NULL, NULL) > 0) {
if (FD_ISSET(sockfd, &readset)) {
/* Accept a new connection request */
socklen_t clilen = sizeof(cli_addr);
int newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) {
perror("server: accept error");
close(newsockfd);
continue; /* We can ignore this */
}
// makes new connection
struct active_connections *new_connection = malloc(sizeof(struct active_connections));
new_connection->socket = newsockfd;
snprintf(new_connection->addr, 20, "%s", inet_ntoa(cli_addr.sin_addr));
new_connection->to_remove = 0;
LIST_INSERT_HEAD(&conn_head, new_connection, connections);
}
// checking connections
struct active_connections *check;
LIST_FOREACH(check, &conn_head, connections) {
int sock = check->socket;
if (FD_ISSET(sock, &readset)) {
char in[MAX] = {'\0'}; // client/server input
ssize_t nread = read(sock, in, MAX);
if (nread <= 0) {
/* Not every error is fatal. Check the return value and act accordingly. */
fprintf(stderr, "%s:%d Error reading froum client\n", __FILE__, __LINE__); //DEBUG
close(sock);
check->to_remove = 1;
continue;
}
else {
char out[MAX]; // directory output
if (in[0] == 'S') {
if (in[1] == 'N') {
struct active_servers *new_server = malloc(sizeof(struct active_servers)); // makes new server
new_server->to_remove = 0; // initializes removal flag
if (sscanf(in+2, "%49[^:]:%d", new_server->name, &new_server->socket) != 2) {
fprintf(stderr, "Error: Failed to parse server name and socket\n");
free(new_server);
continue;
}
snprintf(new_server->addr, 20, "%s", check->addr);
struct active_servers *iter;
int name_taken = 0;
int port_taken = 0;
LIST_FOREACH(iter, &serv_head, connections) {
if (strncmp(iter->name, new_server->name, 50) == 0) { // dup name check/
name_taken = 1;
}
if (iter->socket == new_server->socket) { // dup socket check
port_taken = 1;
}
}
if (name_taken == 1) {
fprintf(stderr, "Error: Server name already taken");
snprintf(out, MAX, "EIError: Server name '%s' is already taken.\n", new_server->name);
write(check->socket, out, MAX);
close(new_server->socket);
free(new_server);
}
else if (port_taken == 1) {
fprintf(stderr, "Error: Server port already taken");
snprintf(out, MAX, "EIError: Server port '%d' is already taken.\n", new_server->socket);
write(check->socket, out, MAX);
close(new_server->socket);
free(new_server);
} else { // finalizes new server
LIST_INSERT_HEAD(&serv_head, new_server, connections);
check->alias = new_server;
check->is_server = 1;
num_of_servs++;
}
}
else if (in[1] == 'D') { // server disconnect (not used rn, see readme)
check->to_remove = 1;
}
}
else if (in[0] == 'C') { // client
if (in[1] == 'Q') { // query
int result = client_query(serv_head, num_of_servs, check);
if (result == -1) {
snprintf(out, MAX, "EIError: Something went wrong. Try again later.\n");
perror("Error: Problem sending query to client");
write(sock, out, MAX);
}
}
else { // nothing else
perror("Error: Invalid Input");
}
check->to_remove = 1; // gets rid of client after query
}
else {
snprintf(out, MAX, "Invalid request");
}
}
}
}
remove_marked_connections(conn_head, serv_head, &num_of_servs); // removes connections to remove
}
else {
// Handle select errors
// Not implemented, see readme
}
}
}