基于UDP的回射服务器研究---未连接UDP漏洞与ICMP错误

来源:互联网 发布:宁有种乎怎么读 编辑:程序博客网 时间:2024/05/29 02:01

UDP回射服务器模型如下图所示


 一、服务器程序分析

代码片段一:

文件:udpserver01.c

#include "unp.h"extern void dg_echo(int, struct sockaddr *, socklen_t clien);int main(int argc, char **argv){    int sockfd;    sockfd = Socket(AF_INET, SOCK_DGRAM, 0);    struct sockaddr_in servaddr, cliaddr;    bzero(&servaddr, sizeof(servaddr));    servaddr.sin_family = AF_INET;    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);    servaddr.sin_port = htons(SERV_PORT);    Bind (sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));    dg_echo(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));    return 0;}

分析:

1、SOCK_DGRAM表示创造一个UDP套接字2

2、将套接字与地址结构绑定起来。

3、主义htonl和htons都是讲主机字节序转化为网络字节序,htonl(转化为32位的网络字节序),htons(转化为16位的网络字节序)。


代码片段二:

文件dg_echo.c

#include "unp.h"#include <stdio.h>void dg_echo(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen){    int n;    socklen_t len;    char buf[MAXLINE];    for(;;){        len = clilen;        n = Recvfrom(sockfd, buf, MAXLINE, 0, pcliaddr, &len);        Sendto(sockfd, buf, n, 0, pcliaddr, len);/*当n=-1的时候会出现错误*/    }}

分析:

1、pcliaddr已经指向一片已经初始化的struct  sockaddr类型的空间。

2、因为UDP套接字中允许返回0字节大小的数据,所以当n = 0时候,也成立。

3、问题:因为sockfd套接字中recvfrom的缓冲区空间是有限的,每个套接字将会按到FIFO的顺序排列的到缓冲区当中,等待读取。然后当缓冲区空间使用完毕的时候,如果在接收的数据报将会摒弃,所以我们可以通过增大缓冲区来解决该问题。增加方式(setsockopt(sockfd,SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof()bufsize))



二、客户端程序分析

代码片段一:

文件udpclient01.c

#include "unp.h"#include <stdio.h>extern void do_cli(FILE*, int, const struct sockaddr*, socklen_t);int main(int argc, char **argv){    int sockfd;    if(argc != 2){            fprintf(stderr, "usage: udpclient<IPaddress>\n ");            return 0;    }    struct sockaddr_in servaddr;    bzero(&servaddr, sizeof(servaddr));    servaddr.sin_family = AF_INET;    servaddr.sin_port = htons(SERV_PORT);    Inet_pton(AF_INET,argv[1], &servaddr.sin_addr);    sockfd = Socket(AF_INET, SOCK_DGRAM, 0);    do_cli(stdin, sockfd,(struct sockaddr*)&servaddr, sizeof(servaddr));    return 0;}
分析:

1、extern 引用另外一个程序中的函数do_cli他表示在本代码文件中其为若字符。

2、inet_pton(int fimaly, const char *strptr, void addrptr),其中addrptr是之值-结果参数, 二Inet_pton是unp.h中对该函数的又一次封装。我们一般调用该函数给struct sockaddr_in结构体中的sin_addr复制。

3、创建UDP套接字


代码片段二:

文件dg_cli.c

#include "unp.h"void do_cli(FILE *fp, int sockfd,const struct sockaddr *pservaddr, socklen_t servlen){    int n;    char buf[MAXLINE];    char recvbuf[MAXLINE];    while(Fgets(buf, MAXLINE, fp) != NULL){        Sendto(sockfd, buf, strlen(buf), 0, pservaddr, servlen);       n =  Recvfrom(sockfd, recvbuf, MAXLINE, 0, NULL, NULL);       recvbuf[n] = 0;/*字符串结尾*/       Fputs(recvbuf, stdout);    }}

分析:

1、通过fget读取数据到buf中,然后发送到UDP服务器去, 然后在接收结果处理后的数据,最后标准输出到屏幕上。

2、因为fputs标准输出为C语言中字符串处理形式,所以字符串结尾必须是‘\0’,不然程序会通过char*访问到不该访问的地方,造成内存泄漏。
3、recvfrom第5,6个参数为NULL, 表示我们并不在乎应答数据报是由谁在发出的。这样就存在一个巨大的问题:任何进程无论是在与本机客户进程相同的主机上还是不同的主机上,都可以向本客户Ip和端口发送数据报,这些数据报被客户读入,都会被认为是服务器的应答数据报。


运行结果分析




三、问题分析

1、针对 (二,代码片段二中分析的第三要点)的问题分析

1)我们可以通过新建一个struct sockaddr结构来记录本次recvfrom的记录,然后再和目的套接字网络地址作比较。

#include "unp.h"void do_cli(FILE *fp, int sockfd,const struct sockaddr *pservaddr, socklen_t servlen){    int n;    char buf[MAXLINE];    char recvbuf[MAXLINE];    socklen_t len;    struct sockaddr *preply_addr;    preply_addr = Malloc(servlen);    while(Fgets(buf, MAXLINE, fp) != NULL){        Sendto(sockfd, buf, strlen(buf), 0, pservaddr, servlen);        len = servlen;        n =  Recvfrom(sockfd, recvbuf, MAXLINE, 0, preply_addr, &len);        if(n >= 0 && (len != servlen || memcmp(pservaddr, preply_addr, len) != 0)){            fprintf(stdout, "replt is from %s (ignored)\n", Sock_ntop(preply_addr, len));        }        recvbuf[n] = 0;        Fputs(recvbuf, stdout);    }}
分析:

1) 我们通过调用dg_cli函数来执行程序,因为该函数的执行是与协议无关的。

2)因为len、preply_addr都是值-结果参数, memcmp是用来比较套接字地址结构本身的。

3)因为协议可能使用可变的套接字结构,所以在调用recvfrom前是len = servlen

4、sock_ntop是对inet_ntop函数的进一步封装。


运行结果:



2、服务器未运行问题

1)当服务器程序未运行的时候,将会返回一个ICMP错误客户进程。

2)ICMP错误是异步错误。该错误是由函数sendto引起的,但是sendto函数本身返回成功。因为UDP输出操作返回仅仅是表示在接口输出队列所在的IP数据报缓冲空间,而ICMP错误是后来返回的,所以这就是它异步的原因。

3)UDP套接字的异步错误,在没有连接的情况下并不会返回自己。

4)根据3)中反而言之, 只有在已连接的情况下,返回的异步错误才会返回的进程。

原创粉丝点击