main.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. #include "includes.h"
  2. #include "base64.h"
  3. #define SERVER_PORT 12345
  4. #define MAX_CONNECTIONS 1000
  5. #define MAX_DIFFERENT_USERS 1024
  6. #define LOCK_FILE_PATH "/tmp/pid.lock"
  7. #define m_string char*
  8. /*
  9. Client
  10. socket()
  11. ? bind() ?
  12. connect()
  13. ----------------
  14. write() | read()
  15. send() | recv()
  16. sendto() | recvfrom()
  17. writev() | readv()
  18. sendmsg() | recvmsg()
  19. ----------------
  20. close()
  21. Server
  22. socket()
  23. bind()
  24. listen()
  25. accept()
  26. ----------------
  27. write() | read()
  28. send() | recv()
  29. sendto() | recvfrom()
  30. writev() | readv()
  31. sendmsg() | recvmsg()
  32. ----------------
  33. close()
  34. */
  35. typedef enum {
  36. eHTTP_UNKNOWN = 0,
  37. eHTTP_CONNECT,
  38. eHTTP_DELETE,
  39. eHTTP_GET,
  40. eHTTP_HEAD,
  41. eHTTP_OPTIONS,
  42. eHTTP_PATCH,
  43. eHTTP_POST,
  44. eHTTP_PUT,
  45. eHTTP_TRACE
  46. } eHTTPMethod;
  47. typedef struct {
  48. eHTTPMethod type;
  49. char path[255];
  50. } sHTTPHeader;
  51. typedef struct {
  52. struct strList *next;
  53. char *cont;
  54. } strList;
  55. typedef struct {
  56. void *access_semaphore;
  57. char login[65];
  58. char password[65];
  59. char bound_ip[17];
  60. char message[2049];
  61. } dbElem;
  62. typedef struct {
  63. int sockd;
  64. struct sockaddr_in client_sockaddr;
  65. int client_sockaddr_size;
  66. void *database;
  67. } clientData;
  68. // === DAEMON BASE FUNCTIONS ===
  69. // Starts server daemon, returns 1 on success, otherwise 0
  70. void start_server();
  71. // Stops server daemon, returns 1 on success, otherwise 0
  72. void stop_server();
  73. // Shows help and returns (is used if no parameter specified)
  74. void show_help();
  75. // Runs server event loop (called inside daemon)
  76. void process_server();
  77. // === SERVER BASE FUNCTIONS ===
  78. // Creates socket, binds it to defined port and makes ready to listen for connections.
  79. int create_socket();
  80. dbElem *getDbElementByUserName(m_string username, dbElem *database);
  81. dbElem *createNewElement(dbElem *database) {
  82. for (int i = 0; i < MAX_DIFFERENT_USERS; ++i) {
  83. if (database[i].login[0] == '\0')
  84. return &database[i];
  85. }
  86. return NULL;
  87. }
  88. dbElem *getDbElementByAssignedIP(m_string ipv4, dbElem *database);
  89. // === WORKER BASE FUNCTIONS ===
  90. // Checking user authentification based on bound ip address to database item or matching login and password;
  91. // Returns 1 and pointer to user dbElem, if auth successfull
  92. // Returns 0 if there's no user with such login
  93. // Returns -1 if credentials are incorrect or there's a user with such login, but passwords don't match
  94. int check_user_authorisation(m_string client_ip, dbElem *database, m_string login, m_string password, dbElem **element);
  95. int getLoginAndPasswordFromRequest(m_string request, m_string*login, m_string*password);
  96. // Tries to update user message. Returns 1, if successfull, or 0 - if not;
  97. int update_user_message(dbElem *user_data, m_string msg, int len);
  98. void send_404_not_found(int sockd);
  99. void send_401_not_authorised(int sockd);
  100. void send_403_forbidden(int sockd);
  101. // Sends normal message
  102. void send_200_message(int sockd, m_string message);
  103. // === WORKER ADDITIONAL FUNCTIONS ===
  104. // Returns client address depending on ipv4/ipv6 protocol
  105. void *get_client_addr(struct sockaddr *);
  106. // Handles request and answers
  107. void *handle_request(void *data);
  108. // Splits http header and returns type and path
  109. void parse_http_request(const char *, sHTTPHeader *);
  110. // ============================================================================================= //
  111. int main(int argc, char **argv) {
  112. if (argc < 2) {
  113. show_help();
  114. return 0;
  115. }
  116. if (strcmp(argv[1], "start") == 0) {
  117. start_server();
  118. return 0;
  119. } else if (strcmp(argv[1], "stop") == 0) {
  120. stop_server();
  121. return 0;
  122. } else if (strcmp(argv[1], "help") == 0) {
  123. show_help();
  124. return 0;
  125. } else {
  126. show_help();
  127. return 0;
  128. }
  129. }
  130. void start_server() {
  131. FILE *lock_file = fopen(LOCK_FILE_PATH, "r");
  132. if (lock_file) {
  133. fprintf(stderr, "Error: seems like server is already running!\nStop it before starting the new one!\n");
  134. return;
  135. }
  136. pid_t pid = fork();
  137. if (pid == -1) {
  138. fprintf(stderr, "Error: cannot create server! (fork exited with error: %s)\n", strerror(errno));
  139. return;
  140. } else if (pid == 0) {
  141. process_server();
  142. return;
  143. } else {
  144. printf("Server started with pid = %d\n", pid);
  145. lock_file = fopen(LOCK_FILE_PATH, "w");
  146. fprintf(lock_file, "%d", pid);
  147. fclose(lock_file);
  148. return;
  149. }
  150. }
  151. void stop_server() {
  152. FILE *lock_file = fopen(LOCK_FILE_PATH, "r");
  153. if (!lock_file) {
  154. fprintf(stderr, "Error: cannot stop server (no running server found)!\n");
  155. return;
  156. }
  157. int pid;
  158. if (fscanf(lock_file, "%d", &pid) != 1) {
  159. fprintf(stderr, "Error: cannot stop server (pid read error)!\n");
  160. fclose(lock_file);
  161. return;
  162. }
  163. if (kill(pid, SIGTERM) != 0) {
  164. fprintf(stderr,
  165. "Warning: server pid is incorrect, server might be already stopped (exited), but lock file still exists.\n");
  166. }
  167. fclose(lock_file);
  168. if (remove(LOCK_FILE_PATH) != 0) {
  169. fprintf(stderr, "Error: cannot remove server lock file (%s), but server was stopped...\n", LOCK_FILE_PATH);
  170. } else {
  171. printf("Server stopped successfully.\n");
  172. }
  173. }
  174. void show_help() {
  175. printf("Using: server <COMMAND>\n"
  176. "\n"
  177. "<COMMAND> values:\n"
  178. "start - starts server daemon, if there's no one already started.\n"
  179. "\n"
  180. "stop - stops running server daemon if there's one.\n"
  181. "\n"
  182. "help - shows this help.\n"
  183. "");
  184. }
  185. int create_socket() {
  186. printf("Creating socket\n");
  187. int sock = socket(PF_INET, SOCK_STREAM, 0);
  188. int on = 1;
  189. setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
  190. /* initialize the server's sockaddr */
  191. struct sockaddr_in server_sockaddr;
  192. memset(&server_sockaddr, 0, sizeof(server_sockaddr));
  193. server_sockaddr.sin_family = AF_INET;
  194. server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  195. server_sockaddr.sin_port = htons(SERVER_PORT);
  196. printf("Binding socket %d to sockaddr %p with size %d\n", sock, (struct sockaddr *) &server_sockaddr,
  197. sizeof(server_sockaddr));
  198. int bind_result = bind(sock, (struct sockaddr *) &server_sockaddr, sizeof(server_sockaddr));
  199. if (bind_result < 0) {
  200. fprintf(stderr, "Server: Error: bind failed!");
  201. return -1;
  202. }
  203. listen(sock, MAX_CONNECTIONS);
  204. return sock;
  205. }
  206. dbElem *getDbElementByUserName(m_string username, dbElem *database) {
  207. for (int i = 0; i < MAX_DIFFERENT_USERS; ++i) {
  208. if (strcmp(database[i].login, username) == 0)
  209. return &database[i];
  210. }
  211. return NULL;
  212. }
  213. dbElem *getDbElementByAssignedIP(m_string ipv4, dbElem *database) {
  214. for (int i = 0; i < MAX_DIFFERENT_USERS; ++i) {
  215. if (strcmp(database[i].bound_ip, ipv4) == 0)
  216. return &database[i];
  217. }
  218. return NULL;
  219. }
  220. void *get_client_addr(struct sockaddr *sa) {
  221. if (sa->sa_family == AF_INET) {
  222. return &(((struct sockaddr_in *) sa)->sin_addr);
  223. }
  224. return &(((struct sockaddr_in6 *) sa)->sin6_addr);
  225. }
  226. void process_server() {
  227. int sock = create_socket();
  228. dbElem *database = malloc(sizeof(dbElem) * MAX_DIFFERENT_USERS);
  229. memset(database, 0, sizeof(dbElem) * MAX_DIFFERENT_USERS);
  230. if (sock < 0) {
  231. fprintf(stderr, "Server: Error, cannot create socket!\n");
  232. return;
  233. }
  234. printf("Server: server created and listening on port %d.\n", SERVER_PORT);
  235. while (1) {
  236. clientData *data = malloc(sizeof(clientData)); // would be freed in thread after its finishing
  237. data->sockd = accept(sock, (struct sockaddr *) &data->client_sockaddr, &data->client_sockaddr_size);
  238. data->database = database;
  239. pthread_t thread_id;
  240. pthread_attr_t attr;
  241. pthread_attr_init(&attr);
  242. printf("Server: got new connection, creating worker thread.\n");
  243. pthread_create(&thread_id, &attr, handle_request, data);
  244. printf("Server: created worker thread %d.\n", thread_id);
  245. }
  246. }
  247. int
  248. check_user_authorisation(m_string client_ip, dbElem *database, m_string login, m_string password, dbElem **element) {
  249. *element = getDbElementByAssignedIP(client_ip, database);
  250. if (*element != NULL) {
  251. return 1;
  252. } else {
  253. if (!login || !password)
  254. return -1;
  255. *element = getDbElementByUserName(login, database);
  256. if (*element == NULL)
  257. return 0;
  258. if (strcmp((*element)->password, password) != 0)
  259. return -1;
  260. strcpy((*element)->bound_ip, login);
  261. }
  262. return 1;
  263. }
  264. int getLoginAndPasswordFromRequest(m_string request, m_string*login_ptr, m_string*password_ptr) {
  265. *login_ptr = *password_ptr = NULL;
  266. char *auth_ptr = strstr(request, "Authorization: Basic ");
  267. if (auth_ptr == NULL) {
  268. return 0;
  269. }
  270. char *login_data = auth_ptr + strlen("Authorization: Basic ");
  271. size_t decoded_login_and_pass_len = 0;
  272. char *data_end = strchr(login_data, '\n');
  273. if (data_end == NULL) {
  274. return 0;
  275. }
  276. char *login_and_password = base64_decode(login_data, data_end - login_data,
  277. &decoded_login_and_pass_len);
  278. if (!login_and_password)
  279. return 0;
  280. char *login = malloc(65);
  281. memset(login, 0, 65);
  282. char *password = malloc(65);
  283. memset(password, 0, 65);
  284. char *delimiter = strchr(login_and_password, ':');
  285. if (delimiter == NULL) {
  286. free(login_and_password);
  287. return 0;
  288. }
  289. if (delimiter - login_and_password > 64) {
  290. free(login_and_password);
  291. return -1; // login is too long!
  292. }
  293. if (decoded_login_and_pass_len - (delimiter + 1 - login_and_password) > 64) {
  294. free(login_and_password);
  295. return -2; // password is too long!
  296. }
  297. memcpy(login, login_and_password, delimiter - login_and_password);
  298. memcpy(password, delimiter + 1, decoded_login_and_pass_len);
  299. printf("USER LOGIN: %s, PASSWORD: %s\n", login, password);
  300. *login_ptr = login;
  301. *password_ptr = password;
  302. return 1; // SUCCESS
  303. }
  304. int update_user_message(dbElem *user_data, m_string msg, int len) {
  305. if (!msg || strlen(msg) == 0)
  306. return 0;
  307. if (strlen(msg) > 2048)
  308. return -1;
  309. memset(user_data->message, 0, 2049);
  310. memcpy(user_data->message, msg, len);
  311. }
  312. void *handle_request(void *data) {
  313. clientData *client_data = data;
  314. char ip[17];
  315. inet_ntop(AF_INET, get_client_addr((struct sockaddr *) &client_data->client_sockaddr), ip, sizeof(ip));
  316. printf("Worker %u: Established connection with %s beginning work.\n", pthread_self(), ip);
  317. const int request_buffer_size = 65536;
  318. char request[request_buffer_size];
  319. int bytes_recvd = recv(client_data->sockd, request, request_buffer_size - 1, 0);
  320. if (bytes_recvd < 0) {
  321. fprintf(stderr, "error recv: %s\n", strerror(errno));
  322. return NULL;
  323. }
  324. request[bytes_recvd] = '\0';
  325. printf("request:\n%s\n", request);
  326. char *login, *password;
  327. int result = getLoginAndPasswordFromRequest(request, &login, &password);
  328. printf("Worker: received login = %s; password = %s\n", login, password);
  329. if (result == -1 || result == -2) {
  330. send_200_message(client_data->sockd, "<h1>Некорректно введены данные пользователя!<br>"
  331. "Максимальная длина логина и пароля - 64 символа!");
  332. close(client_data->sockd);
  333. return NULL;
  334. }
  335. dbElem *element = NULL;
  336. result = check_user_authorisation(ip, client_data->database, login, password, &element);
  337. // This result means that there's record with such login, but password is incorrect
  338. if (result == -1) {
  339. printf("DEBUG: USER AUTH RETURNED -1\n");
  340. send_401_not_authorised(client_data->sockd);
  341. close(client_data->sockd);
  342. return NULL;
  343. }
  344. // This result means that there's no element in database with such credentials - so creating new one.
  345. if (result == 0) {
  346. if (!login || !password) {
  347. printf("DEBUG: USER AUTH RETURNED 0, but NO LOGIN OR PASSWORD\n");
  348. send_401_not_authorised(client_data->sockd);
  349. close(client_data->sockd);
  350. return NULL;
  351. }
  352. element = createNewElement(client_data->database);
  353. if (!element) {
  354. printf("Worker: Warning! Database is full, cannot create more users!\n");
  355. send_401_not_authorised(client_data->sockd);
  356. close(client_data->sockd);
  357. return NULL;
  358. }
  359. printf("Beginning creating element for new user %s with pass %s\n", login, password);
  360. strcpy(element->bound_ip, ip);
  361. strcpy(element->login, login);
  362. strcpy(element->password, password);
  363. strcpy(element->message, "Hello, ");
  364. strcat(element->message, login);
  365. printf("Created element for user %s with password %s and msg %s\n", element->login, element->password, element->message);
  366. }
  367. if (!element) {
  368. printf("DEBUG: NO ELEMENT!!!!\n");
  369. send_401_not_authorised(client_data->sockd);
  370. close(client_data->sockd);
  371. return NULL;
  372. }
  373. // Now assuming, that user is successfully logged in and has valid element
  374. sHTTPHeader req;
  375. parse_http_request(request, &req);
  376. if (req.type == eHTTP_GET) {
  377. if (strstr(req.path, "/logout/") == req.path || strstr(req.path, "/logout") == req.path) {
  378. memset(element->bound_ip, 0, 17);
  379. send_200_message(client_data->sockd, "Successfully logged out");
  380. close(client_data->sockd);
  381. return NULL;
  382. }
  383. if (strstr(req.path, "/updatemsg/") == req.path) {
  384. char *msg_begin = req.path + strlen("/updatemsg/");
  385. char *msg_end = strchr(msg_begin, '/');
  386. int msg_len = strlen(msg_begin);
  387. if (msg_end)
  388. msg_len = (int) (msg_end - msg_begin);
  389. printf("MESSAGE (len = %d): %s", msg_len, msg_begin);
  390. update_user_message(element, msg_begin, msg_len);
  391. }
  392. send_200_message(client_data->sockd, element->message);
  393. } else {
  394. send_404_not_found(client_data->sockd);
  395. }
  396. close(client_data->sockd);
  397. printf("Worker %u: Finished.\n", pthread_self());
  398. return NULL;
  399. }
  400. void parse_http_request(const char *apstrRequest, sHTTPHeader *apHeader) {
  401. int type_length = 0;
  402. char type[255] = {0};
  403. int index = 0;
  404. apHeader->type = eHTTP_UNKNOWN;
  405. sscanf(&apstrRequest[index], "%s", type);
  406. type_length = strlen(type);
  407. if (!strcmp(type, "GET")) {
  408. apHeader->type = eHTTP_GET;
  409. index += type_length + 1;
  410. sscanf(&apstrRequest[index], "%s", apHeader->path);
  411. } else {
  412. if (!strcmp(type, "POST")) {
  413. apHeader->type = eHTTP_POST;
  414. char *pch = strstr(apstrRequest, "\r\n\r\n");
  415. pch += 4;
  416. strcpy(apHeader->path, pch);
  417. }
  418. }
  419. }
  420. void send_200_message(int sockd, m_string message) {
  421. char buffer[65536] = {0};
  422. strcat(buffer, "HTTP/1.1 200 OK\n\n");
  423. strcat(buffer, "<html><body><h1>");
  424. strcat(buffer, message);
  425. strcat(buffer, "</h1></body></html>");
  426. int len = strlen(buffer);
  427. send(sockd, buffer, len, 0);
  428. }
  429. void send_403_forbidden(int sockd) {
  430. const char *buffer = "HTTP/1.1 403 \n\n<h1>Forbidden!<br>Seems like you tried to access data without permission :/</h1>";
  431. int len = strlen(buffer);
  432. send(sockd, buffer, len, 0);
  433. }
  434. void send_404_not_found(int sockd) {
  435. const char *buffer = "HTTP/1.1 404 \n\n<h1>Sorry, nothing was found on your request :(</h1>";
  436. int len = strlen(buffer);
  437. send(sockd, buffer, len, 0);
  438. }
  439. void send_401_not_authorised(int sockd) {
  440. const char *buffer = "HTTP/1.1 401\nWWW-Authenticate: Basic realm=0JTQsNGA0L7QstCwLCDQsdC+0LXRhiEg0JfQsNC70L7Qs9C40L3RjNGB0Y8sINGH0YLQvtCx0Ysg0YPQstC40LTQtdGC0Ywg0YHQvtC+0LHRidC10L3QuNC1\n\n";
  441. int len = strlen(buffer);
  442. send(sockd, buffer, len, 0);
  443. }