Browse Source

新增http server (#265)

* 1.修复了当传入ahci驱动的缓冲区地址为用户缓冲区时,产生的内存越界问题.(采用分配内核缓冲区的方式临时解决)
2.新增http server

* 把libssl-dev添加到bootstrap.sh

* http_server增加对父级相对路径的安全检查,防止访问系统内的其他文件

* 检查空指针情况

* 解决由于链接时,crt*.o未按照升序排列导致init段链接错误的问题
login 1 year ago
parent
commit
660a04cef8

+ 49 - 0
kernel/src/driver/disk/ahci/ahcidisk.rs

@@ -1,6 +1,7 @@
 use super::{_port, hba::HbaCmdTable, virt_2_phys};
 use crate::driver::disk::ahci::HBA_PxIS_TFES;
 use crate::filesystem::mbr::MbrDiskPartionTable;
+use crate::include::bindings::bindings::verify_area;
 use crate::io::{device::BlockDevice, disk_info::Partition, SeekFrom};
 
 use crate::libs::{spinlock::SpinLock, vec_cursor::VecCursor};
@@ -95,6 +96,29 @@ impl AhciDisk {
 
         // 设置数据存放地址
         let mut buf_ptr = buf as *mut [u8] as *mut usize as usize;
+
+        // 由于目前的内存管理机制无法把用户空间的内存地址转换为物理地址,所以只能先把数据拷贝到内核空间
+        // TODO:在内存管理重构后,可以直接使用用户空间的内存地址
+        let user_buf = if unsafe { verify_area(buf_ptr as u64, buf.len() as u64) } {
+            true
+        } else {
+            false
+        };
+        let mut kbuf = if user_buf {
+            let mut x: Vec<u8> = Vec::with_capacity(buf.len());
+            unsafe {
+                x.set_len(buf.len());
+            }
+            Some(x)
+        } else {
+            None
+        };
+
+        if kbuf.is_some() {
+            buf_ptr = kbuf.as_mut().unwrap().as_mut_ptr() as usize;
+        }
+
+
         #[allow(unused_unsafe)]
         let cmdtbl = unsafe {
             (phys_2_virt(volatile_read!(cmdheader.ctba) as usize) as *mut HbaCmdTable)
@@ -177,6 +201,10 @@ impl AhciDisk {
             }
         }
 
+        if kbuf.is_some() {
+            buf.copy_from_slice(kbuf.as_ref().unwrap());
+        }
+
         compiler_fence(core::sync::atomic::Ordering::SeqCst);
         // successfully read
         return Ok(count * 512);
@@ -231,6 +259,27 @@ impl AhciDisk {
         // 设置数据存放地址
         compiler_fence(core::sync::atomic::Ordering::SeqCst);
         let mut buf_ptr = buf as *const [u8] as *mut usize as usize;
+        
+        // 由于目前的内存管理机制无法把用户空间的内存地址转换为物理地址,所以只能先把数据拷贝到内核空间
+        // TODO:在内存管理重构后,可以直接使用用户空间的内存地址
+        let user_buf = if unsafe { verify_area(buf_ptr as u64, buf.len() as u64) } {
+            true
+        } else {
+            false
+        };
+        let mut kbuf = if user_buf {
+            let mut x: Vec<u8> = Vec::with_capacity(buf.len());
+            x.resize(buf.len(), 0);
+            x.copy_from_slice(buf);
+            Some(x)
+        } else {
+            None
+        };
+
+        if kbuf.is_some() {
+            buf_ptr = kbuf.as_mut().unwrap().as_mut_ptr() as usize;
+        }
+
         #[allow(unused_unsafe)]
         let cmdtbl = unsafe {
             (phys_2_virt(volatile_read!(cmdheader.ctba) as usize) as *mut HbaCmdTable)

+ 1 - 1
tools/bootstrap.sh

@@ -43,7 +43,7 @@ install_ubuntu_debian_pkg()
         gnupg \
         lsb-release \
         llvm-dev libclang-dev clang gcc-multilib \
-        gcc build-essential fdisk dosfstools dnsmasq bridge-utils iptables
+        gcc build-essential fdisk dosfstools dnsmasq bridge-utils iptables libssl-dev
 
     if [ -z "$(which docker)" ] && [ -n ${dockerInstall} ]; then
         echo "正在安装docker..."

+ 27 - 0
user/apps/http_server/Makefile

@@ -0,0 +1,27 @@
+CC=$(DragonOS_GCC)/x86_64-elf-gcc
+LD=ld
+OBJCOPY=objcopy
+# 修改这里,把它改为你的relibc的sysroot路径
+RELIBC_OPT=$(DADK_BUILD_CACHE_DIR_RELIBC_0_1_0)
+CFLAGS=-I $(RELIBC_OPT)/include
+
+tmp_output_dir=$(ROOT_PATH)/bin/tmp/user
+output_dir=$(DADK_BUILD_CACHE_DIR_HTTP_SERVER_0_1_0)
+
+LIBC_OBJS:=$(shell find $(RELIBC_OPT)/lib -name "*.o" | sort )
+LIBC_OBJS+=$(RELIBC_OPT)/lib/libc.a
+
+all: main.o
+	mkdir -p $(tmp_output_dir)
+	
+	$(LD) -b elf64-x86-64 -z muldefs -o $(tmp_output_dir)/http_server  $(shell find . -name "*.o") $(LIBC_OBJS) -T link.lds
+
+	$(OBJCOPY) -I elf64-x86-64 -R ".eh_frame" -R ".comment" -O elf64-x86-64 $(tmp_output_dir)/http_server $(output_dir)/http_server.elf
+
+	mv $(output_dir)/http_server.elf $(output_dir)/http_server
+	
+main.o: main.c
+	$(CC) $(CFLAGS) -c main.c  -o main.o
+
+clean:
+	rm -f *.o

+ 239 - 0
user/apps/http_server/link.lds

@@ -0,0 +1,239 @@
+/* Script for -z combreloc */
+/* Copyright (C) 2014-2020 Free Software Foundation, Inc.
+   Copying and distribution of this script, with or without modification,
+   are permitted in any medium without royalty provided the copyright
+   notice and this notice are preserved.  */
+OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
+              "elf64-x86-64")
+OUTPUT_ARCH(i386:x86-64)
+ENTRY(_start)
+
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x20000000) + SIZEOF_HEADERS;
+  .interp         : { *(.interp) }
+  .note.gnu.build-id  : { *(.note.gnu.build-id) }
+  .hash           : { *(.hash) }
+  .gnu.hash       : { *(.gnu.hash) }
+  .dynsym         : { *(.dynsym) }
+  .dynstr         : { *(.dynstr) }
+  .gnu.version    : { *(.gnu.version) }
+  .gnu.version_d  : { *(.gnu.version_d) }
+  .gnu.version_r  : { *(.gnu.version_r) }
+  .rela.dyn       :
+    {
+      *(.rela.init)
+      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+      *(.rela.fini)
+      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
+      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
+      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
+      *(.rela.ctors)
+      *(.rela.dtors)
+      *(.rela.got)
+      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
+      *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*)
+      *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*)
+      *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*)
+      *(.rela.ifunc)
+    }
+  .rela.plt       :
+    {
+      *(.rela.plt)
+      PROVIDE_HIDDEN (__rela_iplt_start = .);
+      *(.rela.iplt)
+      PROVIDE_HIDDEN (__rela_iplt_end = .);
+    }
+  . = ALIGN(CONSTANT (MAXPAGESIZE));
+  .init           :
+  {
+    KEEP (*(SORT_NONE(.init)))
+  }
+  .plt            : { *(.plt) *(.iplt) }
+.plt.got        : { *(.plt.got) }
+.plt.sec        : { *(.plt.sec) }
+  .text           :
+  {
+    *(.text.unlikely .text.*_unlikely .text.unlikely.*)
+    *(.text.exit .text.exit.*)
+    *(.text.startup .text.startup.*)
+    *(.text.hot .text.hot.*)
+    *(.text .stub .text.* .gnu.linkonce.t.*)
+    /* .gnu.warning sections are handled specially by elf.em.  */
+    *(.gnu.warning)
+  }
+  .fini           :
+  {
+    KEEP (*(SORT_NONE(.fini)))
+  }
+  PROVIDE (__etext = .);
+  PROVIDE (_etext = .);
+  PROVIDE (etext = .);
+  . = ALIGN(CONSTANT (MAXPAGESIZE));
+  /* Adjust the address for the rodata segment.  We want to adjust up to
+     the same address within the page on the next page up.  */
+  . = SEGMENT_START("rodata-segment", ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)));
+  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+  .rodata1        : { *(.rodata1) }
+  .eh_frame_hdr   : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
+  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
+  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
+  .gnu_extab   : ONLY_IF_RO { *(.gnu_extab*) }
+  /* These sections are generated by the Sun/Oracle C++ compiler.  */
+  .exception_ranges   : ONLY_IF_RO { *(.exception_ranges*) }
+  /* Adjust the address for the data segment.  We want to adjust up to
+     the same address within the page on the next page up.  */
+  . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
+  /* Exception handling  */
+  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
+  .gnu_extab      : ONLY_IF_RW { *(.gnu_extab) }
+  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
+  .exception_ranges   : ONLY_IF_RW { *(.exception_ranges*) }
+  /* Thread Local Storage sections  */
+  .tdata          :
+   {
+     PROVIDE_HIDDEN (__tdata_start = .);
+     *(.tdata .tdata.* .gnu.linkonce.td.*)
+   }
+  .tbss           : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+  .preinit_array    :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  }
+  .init_array    :
+  {
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
+    KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
+    PROVIDE_HIDDEN (__init_array_end = .);
+  }
+  .fini_array    :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
+    KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  }
+  .ctors          :
+  {
+    /* gcc uses crtbegin.o to find the start of
+       the constructors, so we make sure it is
+       first.  Because this is a wildcard, it
+       doesn't matter if the user does not
+       actually link against crtbegin.o; the
+       linker won't look for a file to match a
+       wildcard.  The wildcard also means that it
+       doesn't matter which directory crtbegin.o
+       is in.  */
+    KEEP (*crtbegin.o(.ctors))
+    KEEP (*crtbegin?.o(.ctors))
+    /* We don't want to include the .ctor section from
+       the crtend.o file until after the sorted ctors.
+       The .ctor section from the crtend file contains the
+       end of ctors marker and it must be last */
+    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
+    KEEP (*(SORT(.ctors.*)))
+    KEEP (*(.ctors))
+  }
+  .dtors          :
+  {
+    KEEP (*crtbegin.o(.dtors))
+    KEEP (*crtbegin?.o(.dtors))
+    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
+    KEEP (*(SORT(.dtors.*)))
+    KEEP (*(.dtors))
+  }
+  .jcr            : { KEEP (*(.jcr)) }
+  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
+  .dynamic        : { *(.dynamic) }
+  .got            : { *(.got) *(.igot) }
+  . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
+  .got.plt        : { *(.got.plt) *(.igot.plt) }
+  .data           :
+  {
+    *(.data .data.* .gnu.linkonce.d.*)
+    SORT(CONSTRUCTORS)
+  }
+  .data1          : { *(.data1) }
+  _edata = .; PROVIDE (edata = .);
+  . = .;
+  __bss_start = .;
+  .bss            :
+  {
+   *(.dynbss)
+   *(.bss .bss.* .gnu.linkonce.b.*)
+   *(COMMON)
+   /* Align here to ensure that the .bss section occupies space up to
+      _end.  Align after .bss to ensure correct alignment even if the
+      .bss section disappears because there are no input sections.
+      FIXME: Why do we need it? When there is no .bss section, we do not
+      pad the .data section.  */
+   . = ALIGN(. != 0 ? 64 / 8 : 1);
+  }
+  .lbss   :
+  {
+    *(.dynlbss)
+    *(.lbss .lbss.* .gnu.linkonce.lb.*)
+    *(LARGE_COMMON)
+  }
+  . = ALIGN(64 / 8);
+  . = SEGMENT_START("ldata-segment", .);
+  .lrodata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
+  {
+    *(.lrodata .lrodata.* .gnu.linkonce.lr.*)
+  }
+  .ldata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
+  {
+    *(.ldata .ldata.* .gnu.linkonce.l.*)
+    . = ALIGN(. != 0 ? 64 / 8 : 1);
+  }
+  . = ALIGN(64 / 8);
+  _end = .; PROVIDE (end = .);
+  . = DATA_SEGMENT_END (.);
+  /* Stabs debugging sections.  */
+  .stab          0 : { *(.stab) }
+  .stabstr       0 : { *(.stabstr) }
+  .stab.excl     0 : { *(.stab.excl) }
+  .stab.exclstr  0 : { *(.stab.exclstr) }
+  .stab.index    0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment       0 : { *(.comment) }
+  .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) }
+  /* DWARF debug sections.
+     Symbols in the DWARF debugging sections are relative to the beginning
+     of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line .debug_line.* .debug_line_end) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+  /* DWARF 3 */
+  .debug_pubtypes 0 : { *(.debug_pubtypes) }
+  .debug_ranges   0 : { *(.debug_ranges) }
+  /* DWARF Extension.  */
+  .debug_macro    0 : { *(.debug_macro) }
+  .debug_addr     0 : { *(.debug_addr) }
+  .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
+  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
+}

+ 238 - 0
user/apps/http_server/main.c

@@ -0,0 +1,238 @@
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define PORT 12580
+#define MAX_REQUEST_SIZE 1500
+#define MAX_RESPONSE_SIZE 1500
+// 网页根目录
+#define WEB_ROOT "/wwwroot/First-WebPage-On-DragonOS"
+#define EXIT_CODE 1
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+#define DEFAULT_PAGE "/index.html"
+
+int security_check(char *path)
+{
+    // 检查路径是否包含 ..
+    if (strstr(path, ".."))
+    {
+        return 0;
+    }
+    return 1;
+}
+
+ssize_t send_response(int sockfd, char *response)
+{
+    return write(sockfd, response, strlen(response));
+}
+
+void send_header(int sockfd, int content_length, char *path)
+{
+    char buffer[MAX_RESPONSE_SIZE];
+    // 获取文件类型
+    char *content_type;
+    if (strstr(path, ".html"))
+    {
+        content_type = "text/html";
+    }
+    else if (strstr(path, ".css"))
+    {
+        content_type = "text/css";
+    }
+    else if (strstr(path, ".js"))
+    {
+        content_type = "application/javascript";
+    }
+    else if (strstr(path, ".png"))
+    {
+        content_type = "image/png";
+    }
+    else if (strstr(path, ".jpg"))
+    {
+        content_type = "image/jpeg";
+    }
+    else if (strstr(path, ".gif"))
+    {
+        content_type = "image/gif";
+    }
+    else
+    {
+        content_type = "text/plain;charset=utf-8";
+    }
+    sprintf(buffer, "HTTP/1.1 200 OK\nContent-Type: %s\nContent-Length: %d\n\n", content_type, content_length);
+    send_response(sockfd, buffer);
+}
+
+void send_file(int sockfd, char *path)
+{
+    printf("send_file: path: %s\n", path);
+
+    int fd = open(path, 0);
+    if (fd == -1)
+    {
+        send_response(
+            sockfd,
+            "HTTP/1.1 404 Not Found\nContent-Type: text/html\n\n<html><body><h1>404 Not Found</h1><p>DragonOS Http Server</p></body></html>");
+        return;
+    }
+
+    int content_length = lseek(fd, 0, SEEK_END);
+    int remaining = content_length;
+    printf("send_file: content_length: %d\n", content_length);
+    lseek(fd, 0, SEEK_SET);
+    send_header(sockfd, content_length, path);
+
+    char buffer[1048576];
+    int readSize;
+    while (remaining)
+    {
+        // 由于磁盘IO耗时较长,所以每次读取1MB,然后再分批发送
+        int to_read = min(1048576, remaining);
+        readSize = read(fd, &buffer, to_read);
+
+        remaining -= readSize;
+        void *p = buffer;
+        while (readSize > 0)
+        {
+            int wsize = write(sockfd, p, min(readSize, MAX_RESPONSE_SIZE));
+            if (wsize <= 0)
+            {
+                printf("send_file failed: wsize: %d\n", wsize);
+                close(fd);
+                return;
+            }
+            p += wsize;
+            readSize -= wsize;
+        }
+    }
+
+    close(fd);
+}
+
+void handle_request(int sockfd, char *request)
+{
+    char *method, *url, *http_version;
+    char path[MAX_REQUEST_SIZE];
+
+    method = strtok(request, " ");
+    url = strtok(NULL, " ");
+    http_version = strtok(NULL, "\r\n");
+    
+    printf("handle_request: method: %s, url: %s, http_version: %s\n", method, url, http_version);
+    // 检查空指针等异常情况
+    if (method == NULL || url == NULL || http_version == NULL)
+    {
+        send_response(sockfd, "HTTP/1.1 400 Bad Request\nContent-Type: text/html\n\n<html><body><h1>400 Bad "
+                              "Request</h1><p>DragonOS Http Server</p></body></html>");
+        return;
+    }
+    // 检查url是否为空
+    if (strlen(url) == 0)
+    {
+        send_response(sockfd, "HTTP/1.1 400 Bad Request\nContent-Type: text/html\n\n<html><body><h1>400 Bad "
+                              "Request</h1><p>DragonOS Http Server</p></body></html>");
+        return;
+    }
+    int default_page = 0;
+    if (url[strlen(url) - 1] == '/')
+    {
+        default_page = 1;
+    }
+
+    if (strcmp(method, "GET") == 0)
+    {
+        if (default_page)
+        {
+            sprintf(path, "%s%s%s", WEB_ROOT, url, DEFAULT_PAGE);
+        }
+        else
+        {
+            sprintf(path, "%s%s", WEB_ROOT, url);
+        }
+        if (!security_check(path))
+        {
+            send_response(
+                sockfd,
+                "HTTP/1.1 403 Forbidden\nContent-Type: text/html\n\n<html><body><h1>403 Forbidden</h1><p>DragonOS Http Server</p></body></html>");
+            return;
+        }
+        send_file(sockfd, path);
+    }
+    else
+    {
+        send_response(sockfd, "HTTP/1.1 501 Not Implemented\nContent-Type: text/html\n\n<html><body><h1>501 Not "
+                              "Implemented</h1><p>DragonOS Http Server</p></body></html>");
+    }
+}
+
+int main(int argc, char const *argv[])
+{
+    int server_fd, new_socket, valread;
+    struct sockaddr_in address;
+    int addrlen = sizeof(address);
+    char buffer[MAX_REQUEST_SIZE] = {0};
+    int opt = 1;
+
+    // 创建socket
+    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
+    {
+        perror("socket failed");
+        exit(EXIT_CODE);
+    }
+
+    // 设置socket选项,允许地址重用
+    // if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)))
+    // {
+    //     perror("setsockopt failed");
+    //     exit(EXIT_CODE);
+    // }
+
+    // 设置地址和端口
+    address.sin_family = AF_INET;
+    address.sin_addr.s_addr = INADDR_ANY;
+    address.sin_port = htons(PORT);
+
+    // 把socket绑定到地址和端口上
+    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0)
+    {
+        perror("bind failed");
+        exit(EXIT_CODE);
+    }
+
+    // 监听socket
+    if (listen(server_fd, 3) < 0)
+    {
+        perror("listen failed");
+        exit(EXIT_CODE);
+    }
+
+    while (1)
+    {
+        printf("Waiting for a client...\n");
+
+        // 等待并接受客户端连接
+        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0)
+        {
+            perror("accept failed");
+            exit(EXIT_CODE);
+        }
+
+        // 接收客户端消息
+        valread = read(new_socket, buffer, MAX_REQUEST_SIZE);
+        printf("%s\n", buffer);
+
+        // 处理请求
+        handle_request(new_socket, buffer);
+
+        // 关闭客户端连接
+        close(new_socket);
+    }
+
+    return 0;
+}

+ 1 - 1
user/apps/test_relibc/Makefile

@@ -8,7 +8,7 @@ CFLAGS=-I $(RELIBC_OPT)/include
 tmp_output_dir=$(ROOT_PATH)/bin/tmp/user
 output_dir=$(DADK_BUILD_CACHE_DIR_TEST_RELIBC_0_1_0)
 
-LIBC_OBJS:=$(shell find $(RELIBC_OPT)/lib -name "*.o")
+LIBC_OBJS:=$(shell find $(RELIBC_OPT)/lib -name "*.o" | sort )
 LIBC_OBJS+=$(RELIBC_OPT)/lib/libc.a
 
 all: main.o

+ 28 - 0
user/dadk/config/http_server-0.1.0.dadk

@@ -0,0 +1,28 @@
+{
+  "name": "Http_Server",
+  "version": "0.1.0",
+  "description": "一个简单的http server",
+  "task_type": {
+    "BuildFromSource": {
+      "Local": {
+        "path": "apps/http_server"
+      }
+    }
+  },
+  "depends": [
+    {
+      "name": "relibc",
+      "version": "0.1.0"
+    }
+  ],
+  "build": {
+    "build_command": "make"
+  },
+  "install": {
+    "in_dragonos_path": "/bin"
+  },
+  "clean": {
+    "clean_command": "make clean"
+  },
+  "envs": []
+}