-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.c
executable file
·390 lines (320 loc) · 9.13 KB
/
main.c
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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
/*
* This file is part of PRO ONLINE.
* PRO ONLINE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* PRO ONLINE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PRO ONLINE. If not, see <http://www.gnu.org/licenses/ .
*/
#include <stdio.h>
#include <string.h>
#include <signal.h>
#if !defined(__APPLE__)
#include <malloc.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <errno.h>
#include <config.h>
#include <user.h>
#include <status.h>
// Server Status
int _status = 0;
// Function Prototypes
void interrupt(int sig);
void enable_address_reuse(int fd);
void change_blocking_mode(int fd, int nonblocking);
int create_listen_socket(uint16_t port);
int server_loop(int server);
/**
* Server Entry Point
* @param argc Number of Arguments
* @param argv Arguments
* @return OS Error Code
*/
int main(int argc, char * argv[])
{
// Result
int result = 0;
// Create Signal Receiver for CTRL + C
signal(SIGINT, interrupt);
// Create Signal Receiver for kill / killall
signal(SIGTERM, interrupt);
// Create Listening Socket
int server = create_listen_socket(SERVER_PORT);
// Created Listening Socket
if(server != -1)
{
// Notify User
printf("Listening for Connections on TCP Port %u.\n", SERVER_PORT);
// Enter Server Loop
result = server_loop(server);
// Notify User
printf("Shutdown complete.\n");
}
// Return Result
return result;
}
/**
* Server Shutdown Request Handler
* @param sig Captured Signal
*/
void interrupt(int sig)
{
// Notify User
printf("Shutting down... please wait.\n");
// Trigger Shutdown
_status = 0;
}
/**
* Enable Address Reuse on Socket
* @param fd Socket
*/
void enable_address_reuse(int fd)
{
// Enable Value
int on = 1;
// Enable Port Reuse
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
}
/**
* Change Socket Blocking Mode
* @param fd Socket
* @param nonblocking 1 for Nonblocking, 0 for Blocking
*/
void change_blocking_mode(int fd, int nonblocking)
{
// Change to Non-Blocking Mode
if(nonblocking) fcntl(fd, F_SETFL, O_NONBLOCK);
// Change to Blocking Mode
else
{
// Get Flags
int flags = fcntl(fd, F_GETFL);
// Remove Non-Blocking Flag
fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
}
}
/**
* Create Port-Bound Listening Socket
* @param port TCP Port
* @return Socket Descriptor
*/
int create_listen_socket(uint16_t port)
{
// Create Socket
int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// Created Socket
if(fd != -1)
{
// Enable Address Reuse
enable_address_reuse(fd);
// Make Socket Nonblocking
change_blocking_mode(fd, 1);
// Prepare Local Address Information
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_addr.s_addr = INADDR_ANY;
local.sin_port = htons(port);
// Bind Local Address to Socket
int bindresult = bind(fd, (struct sockaddr *)&local, sizeof(local));
// Bound Local Address to Socket
if(bindresult != -1)
{
// Switch Socket into Listening Mode
listen(fd, SERVER_LISTEN_BACKLOG);
// Return Socket
return fd;
}
// Notify User
else printf("%s: bind returned %d.\n", __func__, bindresult);
// Close Socket
close(fd);
}
// Notify User
else printf("%s: socket returned %d.\n", __func__, fd);
// Return Error
return -1;
}
/**
* Server Main Loop
* @param server Server Listening Socket
* @return OS Error Code
*/
int server_loop(int server)
{
// Set Running Status
_status = 1;
// Create Empty Status Logfile
update_status();
// Handling Loop
while(_status == 1)
{
// Login Block
{
// Login Result
int loginresult = 0;
// Login Processing Loop
do
{
// Prepare Address Structure
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
memset(&addr, 0, sizeof(addr));
// Accept Login Requests
// loginresult = accept4(server, (struct sockaddr *)&addr, &addrlen, SOCK_NONBLOCK);
// Alternative Accept Approach (some Linux Kernel don't support the accept4 Syscall... wtf?)
loginresult = accept(server, (struct sockaddr *)&addr, &addrlen);
if(loginresult != -1)
{
// Switch Socket into Non-Blocking Mode
change_blocking_mode(loginresult, 1);
}
// Login User (Stream)
if(loginresult != -1) login_user_stream(loginresult, addr.sin_addr.s_addr);
} while(loginresult != -1);
}
// Receive Data from Users
SceNetAdhocctlUserNode * user = _db_user;
while(user != NULL)
{
// Next User (for safe delete)
SceNetAdhocctlUserNode * next = user->next;
// Receive Data from User
int recvresult = recv(user->stream, user->rx + user->rxpos, sizeof(user->rx) - user->rxpos, 0);
// Connection Closed or Timed Out
if(recvresult == 0 || (recvresult == -1 && errno != EAGAIN && errno != EWOULDBLOCK) || get_user_state(user) == USER_STATE_TIMED_OUT)
{
// Logout User
logout_user(user);
}
// Received Data (or leftovers in RX-Buffer)
else if(recvresult > 0 || user->rxpos > 0)
{
// New Incoming Data
if(recvresult > 0)
{
// Move RX Pointer
user->rxpos += recvresult;
// Update Death Clock
user->last_recv = time(NULL);
}
// Waiting for Login Packet
if(get_user_state(user) == USER_STATE_WAITING)
{
// Valid Opcode
if(user->rx[0] == OPCODE_LOGIN)
{
// Enough Data available
if(user->rxpos >= sizeof(SceNetAdhocctlLoginPacketC2S))
{
// Clone Packet
SceNetAdhocctlLoginPacketC2S packet = *(SceNetAdhocctlLoginPacketC2S *)user->rx;
// Remove Packet from RX Buffer
clear_user_rxbuf(user, sizeof(SceNetAdhocctlLoginPacketC2S));
// Login User (Data)
login_user_data(user, &packet);
}
}
// Invalid Opcode
else
{
// Notify User
uint8_t * ip = (uint8_t *)&user->resolver.ip;
printf("Invalid Opcode 0x%02X in Waiting State from %u.%u.%u.%u.\n", user->rx[0], ip[0], ip[1], ip[2], ip[3]);
// Logout User
logout_user(user);
}
}
// Logged-In User
else if(get_user_state(user) == USER_STATE_LOGGED_IN)
{
// Ping Packet
if(user->rx[0] == OPCODE_PING)
{
// Delete Packet from RX Buffer
clear_user_rxbuf(user, 1);
}
// Group Connect Packet
else if(user->rx[0] == OPCODE_CONNECT)
{
// Enough Data available
if(user->rxpos >= sizeof(SceNetAdhocctlConnectPacketC2S))
{
// Cast Packet
SceNetAdhocctlConnectPacketC2S * packet = (SceNetAdhocctlConnectPacketC2S *)user->rx;
// Clone Group Name
SceNetAdhocctlGroupName group = packet->group;
// Remove Packet from RX Buffer
clear_user_rxbuf(user, sizeof(SceNetAdhocctlConnectPacketC2S));
// Change Game Group
connect_user(user, &group);
}
}
// Group Disconnect Packet
else if(user->rx[0] == OPCODE_DISCONNECT)
{
// Remove Packet from RX Buffer
clear_user_rxbuf(user, 1);
// Leave Game Group
disconnect_user(user);
}
// Network Scan Packet
else if(user->rx[0] == OPCODE_SCAN)
{
// Remove Packet from RX Buffer
clear_user_rxbuf(user, 1);
// Send Network List
send_scan_results(user);
}
// Chat Text Packet
else if(user->rx[0] == OPCODE_CHAT)
{
// Enough Data available
if(user->rxpos >= sizeof(SceNetAdhocctlChatPacketC2S))
{
// Cast Packet
SceNetAdhocctlChatPacketC2S * packet = (SceNetAdhocctlChatPacketC2S *)user->rx;
// Clone Buffer for Message
char message[64];
memset(message, 0, sizeof(message));
strncpy(message, packet->message, sizeof(message) - 1);
// Remove Packet from RX Buffer
clear_user_rxbuf(user, sizeof(SceNetAdhocctlChatPacketC2S));
// Spread Chat Message
spread_message(user, message);
}
}
// Invalid Opcode
else
{
// Notify User
uint8_t * ip = (uint8_t *)&user->resolver.ip;
printf("Invalid Opcode 0x%02X in Logged-In State from %s (MAC: %02X:%02X:%02X:%02X:%02X:%02X - IP: %u.%u.%u.%u).\n", user->rx[0], (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], ip[0], ip[1], ip[2], ip[3]);
// Logout User
logout_user(user);
}
}
}
// Move Pointer
user = next;
}
// Prevent needless CPU Overload (1ms Sleep)
usleep(1000);
}
// Free User Database Memory
free_database();
// Close Server Socket
close(server);
// Return Success
return 0;
}