DNSServer和HttpServer

发布于:2024-05-03 ⋅ 阅读:(57) ⋅ 点赞:(0)

DNSServer

#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<netinet/in.h>
#include<sys/socket.h>
#include<unistd.h>
#include<arpa/inet.h>

using namespace std;
const static int dns_server_port = 53;   // DNS服务器的端口,DNS协议标准端口为53
const static char* dns_server_ip = "114.114.114.114";  // DNS服务器的IP地址,这里使用了114DNS

struct dns_header {
    unsigned short id;           // 事务ID,用于标识查询的唯一性
    unsigned short flags;        // 标志位,用于控制查询的行为及响应的状态
    unsigned short questions;    // 问题计数,指示请求中包含的问题数
    unsigned short answer;       // 回答记录数,指示回答部分的DNS记录数
    unsigned short authority;    // 授权记录数,指示授权部分的DNS记录数
    unsigned short additional;   // 附加记录数,指示附加部分的DNS记录数
};

struct dns_question {
    int length;                  // 查询名长度,用于指示查询名的字节长度
    unsigned short qtype;        // 查询类型,指出请求的资源记录类型,如A记录、MX记录等
    unsigned short qclass;       // 查询类别,通常是IN,表示Internet
    unsigned char *name;         // 查询名,表示请求解析的域名
};

int dns_create_header(dns_header *header){
    if(header == nullptr){
        return -1; // 错误处理:指针为空
    }
    srandom(time(nullptr));
    header->id = random();  // 随机生成事务ID
    header->flags = htons(0x0100);  // 设置标志位为标准查询
    header->questions = htons(1);   // 设置问题计数为1
    return 0;
}

int dns_create_question(dns_question *question, const char *domain) {
    if (question == nullptr || domain == nullptr) {
        return -1; // 错误处理:参数为空
    }
    question->qtype = htons(1);  // Type A
    question->qclass = htons(1); // Class IN
    question->name = (unsigned char *)strdup(domain); // 复制域名
    question->length = strlen(domain); // 设置查询名长度

    // 将域名转换为DNS格式
    unsigned char *dst = question->name;
    const char *src = domain;
    while (*src != '\0') {
        unsigned char *len = dst++;
        *len = 0;
        while (*src != '\0' && *src != '.') {
            *dst++ = *src++;
            (*len)++;
        }
        if (*src == '.') {
            src++;
        }
    }
    *dst++ = 0; // 字符串结束
    return 0;
}

int dns_build_requestion(dns_header *header, dns_question *question, char* request) {
    if (header == nullptr || question == nullptr || request == nullptr) {
        return -1; // 错误处理:指针为空
    }
    int offset = 0;
    memcpy(request, header, sizeof(*header)); // 复制头部到请求
    offset = sizeof(dns_header);
    // 复制查询名到请求
    memcpy(request + offset, question->name, question->length);
    offset += question->length;

    memcpy(request + offset, &question->qtype, sizeof(question->qtype));
    offset += sizeof(question->qtype);

    memcpy(request + offset, &question->qclass, sizeof(question->qclass));
    offset += sizeof(question->qclass);

    return offset; // 返回请求总长度
}

int dns_client_commit(const char* domain){
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建UDP套接字
    if(sockfd < 0) {
        return -1; // 错误处理:套接字创建失败
    }

    struct sockaddr_in servaddr{};
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(dns_server_port);
    servaddr.sin_addr.s_addr = inet_addr(dns_server_ip);
    dns_header header{};
    dns_create_header(&header);  // 创建DNS查询头

    dns_question question{};
    if (dns_create_question(&question, domain) != 0) {  // 创建DNS查询问题
        close(sockfd);  // 创建问题失败时,关闭套接字
        return -1;
    }

    char request[1024]{};
    int len = dns_build_requestion(&header, &question, request);  // 构建DNS查询请求

    // 发送DNS查询
    sendto(sockfd, request, len, 0, (struct sockaddr*)&servaddr, sizeof(servaddr));

    // 接收DNS响应
    char response[1024]{};
    struct sockaddr_in addr;
    socklen_t addr_len = sizeof(struct sockaddr_in);

    int n = recvfrom(sockfd, response, sizeof(response), 0, (struct sockaddr*)&addr,                     &addr_len);
    printf("recvfrom:%d %s\n", n, response);  // 打印接收到的字节数和响应内容

    close(sockfd);  // 关闭套接字
    return 0;

HttpServer

#include<iostream>
#include<cstdio>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>
#include<cstdlib>
#include<cstring>
#include<string>
#include<netdb.h>
#include<sys/types.h>
#include<fcntl.h>
#include<vector>

using namespace std;

constexpr static int buffer_size=1024;
const static char* http_version="HTTP/1.1";
const static char* connection_type="connection:close\r\n";

char *host_to_ip(const char *hostname){
    struct hostent *host_entry=gethostbyname(hostname);
    if (host_entry) {
        return inet_ntoa(*(struct in_addr*)host_entry->h_addr_list[0]);
    }
    return nullptr;
}

int http_create_socket(const char *ip){
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    struct sockaddr_in sin{};
    sin.sin_family=AF_INET;
    sin.sin_port=htons(80);
    sin.sin_addr.s_addr=inet_addr(ip);

    if(0!=connect(sockfd,(struct sockaddr*)&sin,sizeof(sockaddr_in))){
        close(sockfd);
        return -1;
    }
    fcntl(sockfd,F_SETFL,O_NONBLOCK);
    return sockfd;
}

char* http_send_request(const char* hostname,const char* resource){
    char *ip=host_to_ip(hostname);
    int sockfd=http_create_socket(ip);

    char buffer[buffer_size]{};
    sprintf(buffer, "GET %s %s\r\nHost: %s\r\n%s\r\n", resource, http_version, hostname, connection_type);

    send(sockfd,buffer,strlen(buffer),0);
    //select
    fd_set fdread;
    FD_ZERO(&fdread);
    FD_SET(sockfd,&fdread);

    struct timeval tv;
    tv.tv_sec=5;
    tv.tv_usec=0;

    vector<char>result;
    while(1){
        int selection=select(sockfd+1,&fdread,nullptr,nullptr,&tv);
        if(!selection||FD_ISSET(sockfd,&fdread)){
            break;
        }
        else{
            char buffer[buffer_size]{};
            int len=recv(sockfd,buffer,buffer_size,0);
            if(len==0){
                break;
            }
            result.insert(result.end(),buffer,buffer+len);
        }
    }
    result.push_back('\0');
    // 如果需要返回一个 C 风格的字符串,可以使用下面的代码将 vector 转换为字符串
    result.push_back('\0');  // 确保有结束符
    char* c_result = new char[result.size()];
    std::memcpy(c_result, result.data(), result.size());

    return c_result;  // 注意:调用者负责释放这段内存

}

int main(){
    const char* hostname = "httpbin.org";
    const char* resource = "/get";

    char* response = http_send_request(hostname, resource);
    if (response) {
        std::cout << "Received Response:\n" << response << std::endl;
        delete[] response; // 释放内存
    } else {
        std::cerr << "Failed to get a response from the server." << std::endl;
    }

    return 0;
}