I'm trying to implement basic socket program in C which parses HTTP GET requests for now, in parse_http_req function I've declared char* line; and after strncpy call line contains 1st line of HTTP request data. My doubt is I didn't allocate any memory for line before I called strncpy, how is this code working correctly ? Here is my full server.c code (still under development)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <unistd.h>
#include <asm-generic/socket.h>
#include <string.h>
#define DEFAULT_PORT 8080
#define MAX_REQ_QUEUE_SIZE 10
#define MAX_REQ_SIZE 1024
#define DEFAULT_PROTOCOL "http"
enum Methods {
GET,
POST,
PUT,
OPTIONS,
DELETE,
INVALID_METHOD
};
typedef struct Request {
enum Methods method;
char *path;
char *protocol;
char *body;
size_t content_length;
} Request;
enum Methods get_req_method(char *req) {
if (strcmp(req, "GET")) {
return GET;
}
if (strcmp(req, "POST")) {
return POST;
}
if(strcmp(req, "OPTONS")){
return OPTIONS;
}
if (strcmp(req, "PUT")) {
return PUT;
}
if (strcmp(req, "DELETE")) {
return DELETE;
}
return INVALID_METHOD;
}
void parse_http_req(char *req, size_t req_size) {
int size = 0;
while ((*(req + size) != '\n') && (size < req_size)) {
size++;
}
printf("size: %d\n", size);
char *line;
line = strncpy(line, req, size);
line[size] = '\0';
printf("First line: %s\n", line);
}
void accept_incoming_request(int socket, struct sockaddr_in *address, socklen_t len) {
int accept_fd;
socklen_t socklen = len;
while ((accept_fd = accept(socket, (struct sockaddr *)address, &len)) > 0) {
char *buffer = (char *)malloc(sizeof(char) * MAX_REQ_SIZE);
int recvd_bytes = read(accept_fd, buffer, MAX_REQ_SIZE);
printf("%s\n%ld\n%d\n\n\n", buffer, strlen(buffer), recvd_bytes);
parse_http_req(buffer, strlen(buffer));
free(buffer);
send(accept_fd, "hello", 5, 0);
close(accept_fd);
}
shutdown(socket, SHUT_RDWR);
}
int create_socket(struct sockaddr_in *address, socklen_t len) {
int fd, opt = 1;
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket creation failed\n");
return -1;
}
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
return -1;
}
if (bind(fd, (struct sockaddr*)address, len) < 0) {
perror("Socket bind failed\n");
return -1;
}
return fd;
}
int main(int argc, char *argv[]) {
short PORT = DEFAULT_PORT;
char *PROTOCOL = DEFAULT_PROTOCOL;
if (argc > 1) {
PORT = (short)atoi(argv[1]);
}
if (argc > 2) {
PROTOCOL = argv[2];
}
printf("%d %s\n", PORT, PROTOCOL);
struct sockaddr_in address = {
sin_family: AF_INET,
sin_addr: {
s_addr: INADDR_ANY
},
sin_port: htons(PORT)
};
int socket;
if ((socket = create_socket(&address, sizeof(address))) < 0) {
exit(1);
}
if (listen(socket, MAX_REQ_QUEUE_SIZE) < 0) {
perror("Listen failed\n");
exit(1);
}
accept_incoming_request(socket, &address, sizeof(address));
return 0;
}
If you compile and run server starts at port: 8080 or you can pass desired port as 1st arg like ./server 9000
To test: wget http://localhost:8080/dummy/path
I tried to see if that line strncpy piece of code is valid. Here is my test.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void f1(char *l2, int size) {
char *l1;
l1 = strncpy(l1, l2, size);
printf("%s\n", l1);
}
void main() {
char *l1 = malloc(10);
char *l2 = "test";
strncpy(l1, l2, 5);
printf("%s\n", l1);
free(l1);
f1(l2, 5);
}
As I expected if I allocate some memory for l1 before calling strncpy like in main it's all good. But, getting Segmentation fault (core dumped) while executing function f1. How is similar piece of code working in my server.c but not in this test.c program
The code
char *line; line = strncpy(line, req, size);has undefined behavior:lineis an uninitialized pointer, so you cannot copy anything to it.My recommendation is you should never use
strncpy: it does not do what you think.In your code, you should instead use
char *line = strndup(req, size);which allocates memory and copies the string fragment to it. The memory should be freed after use withfree(line).strndup()first standardized in POSIX finally becomes part of the C Standard in the latest version, so it is available on most systems, but it your target does not have it, it can be defined this way:There are other problems in your code:
strcmp(req, "GET")returns0is the strings have the same characters, so you should write: orwhile((*(req + size) != '\n') && (size < req_size))to avoid accessingreq[req_size].in
accept_incoming_requestyou should allocate the destination array with an extra byte for the null terminator and set this null byte at the end of the received packet with: