123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <netdb.h>
- #include <time.h>
- #include <ctype.h>
- #include <sys/socket.h>
- #include <sys/types.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <pthread.h>
- #include <signal.h>
- #define SERVER_PORT 12345
- #define MAX_CONNECTIONS 1000
- #define LOCK_FILE_PATH "/tmp/pid.lock"
- #define m_string char*
- /*
- Client
- socket()
- ? bind() ?
- connect()
- ----------------
- write() | read()
- send() | recv()
- sendto() | recvfrom()
- writev() | readv()
- sendmsg() | recvmsg()
- ----------------
- close()
- Server
- socket()
- bind()
- listen()
- accept()
- ----------------
- write() | read()
- send() | recv()
- sendto() | recvfrom()
- writev() | readv()
- sendmsg() | recvmsg()
- ----------------
- close()
- */
- typedef enum {
- eHTTP_UNKNOWN = 0,
- eHTTP_CONNECT,
- eHTTP_DELETE,
- eHTTP_GET,
- eHTTP_HEAD,
- eHTTP_OPTIONS,
- eHTTP_PATCH,
- eHTTP_POST,
- eHTTP_PUT,
- eHTTP_TRACE
- } eHTTPMethod;
- typedef struct {
- eHTTPMethod type;
- char path[255];
- } sHTTPHeader;
- typedef struct {
- struct strList *next;
- char *cont;
- } strList;
- typedef struct {
- void *access_semaphore;
- char login[256];
- char password[256];
- char bound_ip[64];
- char message[4096];
- } dbElem;
- typedef struct {
- int sockd;
- struct sockaddr_in client_sockaddr;
- int client_sockaddr_size;
- } clientData;
- // DAEMON BASE FUNCTIONS
- // Starts server daemon, returns 1 on success, otherwise 0
- void start_server();
- // Stops server daemon, returns 1 on success, otherwise 0
- void stop_server();
- // Shows help and returns (is used if no parameter specified)
- void show_help();
- // Runs server event loop (called inside daemon)
- void process_server();
- // SERVER BASE FUNCTIONS
- // Creates socket, binds it to defined port and makes ready to listen for connections.
- int create_socket();
- // Opens (and creates, if necessary) file for user data and maps it to pointer in shared mode via memory-mapping.
- void *open_database();
- // Creates worker subprocess for connection, which lives while connection is established and communicates with client.
- // Returns worker pid to parent soon after creation (non-blocking communication).
- int process_request(int client_d, struct sockaddr *addr, void *database);
- // WORKER BASE FUNCTIONS
- // Waits until user request, then returns path, client_ip and header information
- void receive_request(m_string*path, m_string*client_ip, m_string*http_header);
- // Checks if user ip is bound to any login in database. Returns username, if bound and nullptr - if not.
- m_string check_user_authorisation(m_string client_ip);
- // Tries to update user message. Returns 1, if successfull, or 0 - if not;
- int update_user_message(m_string username, m_string msg);
- void send_404_not_found(int sockd);
- void send_401_not_authorised(int sockd);
- void send_403_forbidden(int sockd);
- void send_200_user_message(int sockd);
- void send_error_invalid_message(int sockd);
- // WORKER ADDITIONAL FUNCTIONS
- // Sends message
- void send_message(int sockd, const m_string msg);
- // Returns client address depending on ipv4/ipv6 protocol
- void *get_client_addr(struct sockaddr *);
- // Parses http request
- void* handle_request(void* data);
- void parse_http_request(const char *, sHTTPHeader *);
- // ============================================================================================= //
- int main(int argc, char** argv) {
- if (argc < 2) {
- show_help();
- return 0;
- }
- if (strcmp(argv[1], "start") == 0) {
- start_server();
- return 0;
- } else if (strcmp(argv[1], "stop") == 0) {
- stop_server();
- return 0;
- } else if (strcmp(argv[1], "help") == 0) {
- show_help();
- return 0;
- } else {
- show_help();
- return 0;
- }
- }
- void start_server() {
- FILE *lock_file = fopen(LOCK_FILE_PATH, "r");
- if (lock_file) {
- fprintf(stderr, "Error: seems like server is already running!\nStop it before starting the new one!\n");
- return;
- }
- pid_t pid = fork();
- if (pid == -1) {
- fprintf(stderr, "Error: cannot create server! (fork exited with error: %s)\n", strerror(errno));
- return;
- } else if (pid == 0) {
- process_server();
- return;
- } else {
- printf("Server started with pid = %d\n", pid);
- lock_file = fopen(LOCK_FILE_PATH, "w");
- fprintf(lock_file, "%d", pid);
- fclose(lock_file);
- return;
- }
- }
- void stop_server() {
- FILE *lock_file = fopen(LOCK_FILE_PATH, "r");
- if (!lock_file) {
- fprintf(stderr, "Error: cannot stop server (no running server found)!\n");
- return;
- }
- int pid;
- if (fscanf(lock_file, "%d", &pid) != 1) {
- fprintf(stderr, "Error: cannot stop server (pid read error)!\n");
- fclose(lock_file);
- return;
- }
- if (kill(pid, SIGTERM) != 0) {
- fprintf(stderr, "Warning: server pid is incorrect, server might be already stopped (exited), but lock file still exists.\n");
- }
- fclose(lock_file);
- if (remove(LOCK_FILE_PATH) != 0) {
- fprintf(stderr, "Error: cannot remove server lock file (%s), but server was stopped...\n", LOCK_FILE_PATH);
- } else {
- printf("Server stopped successfully.\n");
- }
- }
- void show_help() {
- printf("Using: server <COMMAND>\n"
- "\n"
- "<COMMAND> values:\n"
- "start - starts server daemon, if there's no one already started.\n"
- "\n"
- "stop - stops running server daemon if there's one.\n"
- "\n"
- "help - shows this help.\n"
- "");
- }
- void * get_client_addr(struct sockaddr * sa) {
- if (sa -> sa_family == AF_INET) {
- return &(((struct sockaddr_in * ) sa) -> sin_addr);
- }
- return &(((struct sockaddr_in6 * ) sa) -> sin6_addr);
- }
- void process_server() {
- int sock = create_socket();
- if (sock < 0) {
- fprintf(stderr, "Server: Error, cannot create socket!\n");
- return;
- }
- printf("Server: server created and listening on port %d.\n", SERVER_PORT);
- while (1) {
- clientData *data = malloc(sizeof(clientData)); // would be freed in thread after its finishing
- data->sockd = accept(sock, (struct sockaddr *) &data->client_sockaddr, &data->client_sockaddr_size);
- pthread_t thread_id;
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- printf("Server: got new connection, creating worker thread.\n");
- pthread_create(&thread_id, &attr, handle_request, data);
- printf("Server: created worker thread %d.\n", thread_id);
- }
- }
- int create_socket() {
- printf("Creating socket\n");
- int sock = socket(PF_INET, SOCK_STREAM, 0);
- int on = 1;
- setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
- /* initialize the server's sockaddr */
- struct sockaddr_in server_sockaddr;
- memset(&server_sockaddr, 0, sizeof(server_sockaddr));
- server_sockaddr.sin_family = AF_INET;
- server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
- server_sockaddr.sin_port = htons(SERVER_PORT);
- printf("Binding socket %d to sockaddr %p with size %d\n", sock, (struct sockaddr *)&server_sockaddr, sizeof(server_sockaddr));
- int bind_result = bind(sock, (struct sockaddr *)&server_sockaddr, sizeof(server_sockaddr));
- if (bind_result < 0) {
- fprintf(stderr, "Server: Error: bind failed!");
- return -1;
- }
- listen(sock, MAX_CONNECTIONS);
- return sock;
- }
- void* handle_request(void* data) {
- clientData* client_data = data;
- char ip[INET_ADDRSTRLEN];
- inet_ntop(AF_INET, get_client_addr((struct sockaddr *) &client_data->client_sockaddr), ip, sizeof(ip));
- printf("Worker %d: Established connection with %s beginning work.\n", pthread_self(), ip);
- const int request_buffer_size = 65536;
- char request[request_buffer_size];
- int bytes_recvd = recv(client_data->sockd, request, request_buffer_size - 1, 0);
- if (bytes_recvd < 0) {
- fprintf(stderr, "error recv: %s\n", strerror(errno));
- return NULL;
- }
- request[bytes_recvd] = '\0';
- printf("request:\n%s\n", request);
- sHTTPHeader req;
- parse_http_request(request, &req);
- if (req.type == eHTTP_GET) {
- send_message(client_data->sockd, "Hello! GET received...");
- } else if (req.type == eHTTP_POST) {
- send_message(client_data->sockd, "Hello! POST received...");
- } else {
- send_404_not_found(client_data->sockd);
- }
- close(client_data->sockd);
- printf("Worker %d: Finished.\n", pthread_self());
- return NULL;
- }
- void parse_http_request(const char *apstrRequest, sHTTPHeader *apHeader) {
- int type_length = 0;
- char type[255] = {0};
- int index = 0;
- apHeader->type = eHTTP_UNKNOWN;
- sscanf(&apstrRequest[index], "%s", type);
- type_length = strlen(type);
- if (!strcmp(type, "GET")) {
- apHeader->type = eHTTP_GET;
- index += type_length + 1;
- sscanf(&apstrRequest[index], "%s", apHeader->path);
- } else {
- if (!strcmp(type, "POST")) {
- apHeader->type = eHTTP_POST;
- char *pch = strstr(apstrRequest, "\r\n\r\n");
- pch += 4;
- strcpy(apHeader->path, pch);
- }
- }
- }
- void send_message(int aSock, const char *apstrMessage) {
- char buffer[65536] = {0};
- strcat(buffer, "HTTP/1.1 200 OK\n\n");
- strcat(buffer, "<h1>");
- strcat(buffer, apstrMessage);
- strcat(buffer, "</h1>");
- int len = strlen(buffer);
- send(aSock, buffer, len, 0);
- }
- void send_404_not_found(int sockd) {
- const char *buffer = "HTTP/1.1 404 \n\n";
- int len = strlen(buffer);
- send(sockd, buffer, len, 0);
- }
- // server: got connection from 127.0.0.1
- // request:
- // GET /index.html HTTP/1.1
- // Host: localhost:3490
- // Connection: keep-alive
- // Upgrade-Insecure-Requests: 1
- // User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/68.0.3440.75 Chrome/68.0.3440.75 Safari/537.36
- // Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
- // Accept-Encoding: gzip, deflate, br
- // Accept-Language: en-US,en;q=0.9,ru;q=0.8
|