-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathServerGateway.cpp
More file actions
194 lines (173 loc) · 6.34 KB
/
ServerGateway.cpp
File metadata and controls
194 lines (173 loc) · 6.34 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
// Server side C/C++ program to demonstrate Socket programming
// ports: Peter 12117, Jara 12115, Mark 12104
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <iostream>
#include "MainMenu.cpp"
#include "StringParser.h"
#include "ServerStats.h"
#include "ThreadContext.h"
// next line from MM
// #include "assert.h"
#define PORT 12104
using namespace std;
ServerStats serverStats;
//TODO check this for mutex lock
// Prepares a way for a thread to be sent to a dynamically created menu.
void *threadToMenu(void *arg) {
int *socketPtr = (int *) arg;
// stores client context
ThreadContext *context = new ThreadContext();
MainMenu *mainMenuPtr;
mainMenuPtr = new MainMenu(*socketPtr);
mainMenuPtr->loopThread(serverStats, *context);
delete context;
delete mainMenuPtr;
serverStats.decrementNumActiveClients();
pthread_exit(NULL);
}
// Connects the client socket to the server socket
// TODO candidate
// return 0 if password/username passed.
// return -2 if incorrect password
// return -1 if username does not exist
int passwordVaultStub(char *username, char *password) {
const char *CORRECT_UN = "mike";
const char *CORRECT_PW = "123";
if (strcmp(username, CORRECT_UN) == 0) {
if (strcmp(password, CORRECT_PW) == 0)
return 0;
else
return -2;
} else
return -1;
}
// Authenticates user credentials
// returns a -1 for bad username
// returns -2 for bad password
// returns -3 for bad RPC
// returns 0 if passed
int authenticate(char *buffer, StringParser &parser) {
KeyValue rpcKV; // "rpc=[exact_rpc]"
KeyValue usernameKV;
KeyValue passwordKV;
parser.newRPC(buffer);
parser.getNextKeyValue(rpcKV);
parser.getNextKeyValue(usernameKV);
parser.getNextKeyValue(passwordKV);
if ((strcmp(rpcKV.getKey(), "rpc") != 0) ||
(strcmp(rpcKV.getValue(), "connect") != 0))
{
return -3; // represents bad rpc
}
char *username = usernameKV.getValue();
char *password = passwordKV.getValue();
// Returns a -1 for bad username, -2 for bad password, -3 for bad RPC, 0 if passed.
int retValue = passwordVaultStub(username, password);
cout << retValue << endl;
return retValue;
}
// Sends a message to client, and then closes the socket assigned to current client.
// return 0 if successful
// return -1 if failed
int disconnect(int socket_num, char *buff){
// char disconnectMsg[1024] = {0};
send(socket_num, buff, strlen(buff) , 0 );
// close active socket
return close(socket_num);
}
// Establishes a socket connection
int startServer(struct sockaddr_in m_address)
{
int m_port = PORT;
int opt = 1;
int m_server_fd;
// Creating socket file descriptor
if ((m_server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
// Helps manipulate socket options referred by the file descriptor sockfd.
// Helps in reuse of address and port. Prevents error such as: “address already in use”.
if (setsockopt(m_server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
&opt, sizeof(opt)))
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
m_address.sin_family = AF_INET;
m_address.sin_addr.s_addr = INADDR_ANY;
m_address.sin_port = (uint16_t) htons((uint16_t) m_port);
// bind socket to address and port number
if (bind(m_server_fd, (struct sockaddr *)&m_address,
sizeof(m_address)) < 0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
return m_server_fd;
}
// Runs the server program
int main(int argc, char const *argv[])
{
const int MAX_CLIENTS = 10;
int server_fd, new_socket;
struct sockaddr_in m_address;
// int opt = 1;
int addrlen = sizeof(m_address);
char buffer[1024] = {0};
char DISCONNECT_RPC[1024] = "rpc=disconnect;";
StringParser *parser = new StringParser;
KeyValue rpcKeyValue;
// Server creates socket file descriptor, sets socket options, and binds socket
server_fd = startServer(m_address);
// To store thread
pthread_t singleThread;
// This loop is the server remaining active
while (true) {
cout << "Waiting for connection" << endl << "... ... ... ..." << endl;
// Establish a new connection.
if (listen(server_fd, 3) < 0) {
cout << endl << "Listen error" << endl;
perror("listen");
exit(EXIT_FAILURE);
}
// if number of clients exceeds the maximum, then new incoming client will be disconnected.
if (serverStats.getNumActiveClients() >= MAX_CLIENTS) {
read(new_socket, buffer, 1024);
disconnect(new_socket, DISCONNECT_RPC);
} else {
// new_socket is where we'll be communicating to the client
if ((new_socket = accept(server_fd, (struct sockaddr *)&m_address,
(socklen_t*)&addrlen))<0) {
cout << endl << "accept error" << endl;
perror("accept");
exit(EXIT_FAILURE);
}
// Username and password RPC should be sent in here.
read(new_socket, buffer, 1024);
// A password is sent into this, and out of it comes a response: password is good or bad
int connectReturn = -4; // -4 represents a disconnect (due to too many clients, for example)
connectReturn = authenticate(buffer, *parser);
cout << "Login result: " << connectReturn << endl;
if (connectReturn < 0) {
// client disconnected due to bad credentials
connectReturn = disconnect(new_socket, DISCONNECT_RPC);
} else {
// client remains connected
// In a function create dynamic mainmenu, and populate it with a single thread
// FIXME: have this check for a return from pthread_create, in case a new pthread can't be created.
serverStats.incrementNumActiveClients();
pthread_create(&singleThread, NULL, threadToMenu, (void *) &new_socket);
send(new_socket , buffer , strlen(buffer) , 0 );
}
cout << "Thread created, returning to listening state" << endl;
}
}
return 0;
}