Unix 网络编程说明
Unix Network Programming Clarification
当我偶然发现这个程序(第 6.8 节,第 179-180 页)时,我正在阅读经典书籍 Unix Network Programming
#include "unp.h"
int
main(int argc, char **argv)
{
int i, maxi, maxfd, listenfd, connfd, sockfd;
int nready, client[FD_SETSIZE];
ssize_t n;
fd_set rset, allset;
char buf[MAXLINE];
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
maxfd = listenfd; /* initialize */
maxi = -1; /* index into client[] array */
for (i = 0; i < FD_SETSIZE; i++)
client[i] = -1; /* -1 indicates available entry */
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
for ( ; ; ) {
rset = allset; /* structure assignment */
nready = Select(maxfd+1, &rset, NULL, NULL, NULL);
if (FD_ISSET(listenfd, &rset)) { /* new client connection */
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
for (i = 0; i < FD_SETSIZE; i++)
if (client[i] < 0) {
client[i] = connfd; /* save descriptor */
break;
}
if (i == FD_SETSIZE)
err_quit("too many clients");
FD_SET(connfd, &allset); /* add new descriptor to set */
if (connfd > maxfd)
maxfd = connfd; /* for select */
if (i > maxi)
maxi = i; /* max index in client[] array */
if (--nready <= 0)
continue; /* no more readable descriptors */
}
for (i = 0; i <= maxi; i++) { /* check all clients for data */
if ( (sockfd = client[i]) < 0)
continue;
if (FD_ISSET(sockfd, &rset)) {
if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {
/*4connection closed by client */
Close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -1;
} else
Writen(sockfd, buf, n);
if (--nready <= 0)
break; /* no more readable descriptors */
}
}
}
}
作者提到此程序无法抵御 DOS 攻击。引自书中,
"Unfortunately, there is a problem with the server that we just showed. Consider what happens if a malicious client connects to the server, sends one byte of data (other than a newline), and then goes to sleep. The server will call read (system call), which will read the the single byte of data from the client and then block in the next call to read, waiting for more data from this client. The server is then blocked by this one client, and will not service any other clients until malicious client either sends a newline or terminates"
我不确定我是否理解正确。为什么这个恶意客户端会第二次调用 read 系统调用,因为它只发送了 1 个字节的数据,第一次调用 [= 时会得到通知48=]。后续调用 select 将永远不会设置此恶意文件描述符,因为没有 activity。我在这里遗漏了什么吗?
我这里猜测是代码有错别字,不是Read,应该是书中其他地方提到的某个版本的Readline方法。
注意:代码中包含Read和Select(带大写R 和 S),它们只不过是 read 和 select 系统调用
的错误处理包装器
我认为他指出的问题是,正如您在注释中指出的那样,此代码使用 Read
,它是 read
的包装器。我的猜测是 Read
将尝试再次调用 read
以完成接收永远不会到来的数据,因为我现在不打算翻出我的书本。
是的,它的本意似乎是 Readline
。
在downloadable source code that file is tcpcliserv/tcpservselect01.c
and there is a corresponding .lc
file (with line number annotations) which uses Readline
instead of Read
, and it was Readline
in the second edition of the book (source code)。理解括号注释“(换行符除外)”的唯一方法是假设预期的读取函数读取到换行符。
奇怪的是,还没有报道 in the errata。也许你应该这样做。
当我偶然发现这个程序(第 6.8 节,第 179-180 页)时,我正在阅读经典书籍 Unix Network Programming
#include "unp.h"
int
main(int argc, char **argv)
{
int i, maxi, maxfd, listenfd, connfd, sockfd;
int nready, client[FD_SETSIZE];
ssize_t n;
fd_set rset, allset;
char buf[MAXLINE];
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
maxfd = listenfd; /* initialize */
maxi = -1; /* index into client[] array */
for (i = 0; i < FD_SETSIZE; i++)
client[i] = -1; /* -1 indicates available entry */
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
for ( ; ; ) {
rset = allset; /* structure assignment */
nready = Select(maxfd+1, &rset, NULL, NULL, NULL);
if (FD_ISSET(listenfd, &rset)) { /* new client connection */
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
for (i = 0; i < FD_SETSIZE; i++)
if (client[i] < 0) {
client[i] = connfd; /* save descriptor */
break;
}
if (i == FD_SETSIZE)
err_quit("too many clients");
FD_SET(connfd, &allset); /* add new descriptor to set */
if (connfd > maxfd)
maxfd = connfd; /* for select */
if (i > maxi)
maxi = i; /* max index in client[] array */
if (--nready <= 0)
continue; /* no more readable descriptors */
}
for (i = 0; i <= maxi; i++) { /* check all clients for data */
if ( (sockfd = client[i]) < 0)
continue;
if (FD_ISSET(sockfd, &rset)) {
if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {
/*4connection closed by client */
Close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -1;
} else
Writen(sockfd, buf, n);
if (--nready <= 0)
break; /* no more readable descriptors */
}
}
}
}
作者提到此程序无法抵御 DOS 攻击。引自书中,
"Unfortunately, there is a problem with the server that we just showed. Consider what happens if a malicious client connects to the server, sends one byte of data (other than a newline), and then goes to sleep. The server will call read (system call), which will read the the single byte of data from the client and then block in the next call to read, waiting for more data from this client. The server is then blocked by this one client, and will not service any other clients until malicious client either sends a newline or terminates"
我不确定我是否理解正确。为什么这个恶意客户端会第二次调用 read 系统调用,因为它只发送了 1 个字节的数据,第一次调用 [= 时会得到通知48=]。后续调用 select 将永远不会设置此恶意文件描述符,因为没有 activity。我在这里遗漏了什么吗?
我这里猜测是代码有错别字,不是Read,应该是书中其他地方提到的某个版本的Readline方法。
注意:代码中包含Read和Select(带大写R 和 S),它们只不过是 read 和 select 系统调用
的错误处理包装器我认为他指出的问题是,正如您在注释中指出的那样,此代码使用 Read
,它是 read
的包装器。我的猜测是 Read
将尝试再次调用 read
以完成接收永远不会到来的数据,因为我现在不打算翻出我的书本。
是的,它的本意似乎是 Readline
。
在downloadable source code that file is tcpcliserv/tcpservselect01.c
and there is a corresponding .lc
file (with line number annotations) which uses Readline
instead of Read
, and it was Readline
in the second edition of the book (source code)。理解括号注释“(换行符除外)”的唯一方法是假设预期的读取函数读取到换行符。
奇怪的是,还没有报道 in the errata。也许你应该这样做。