本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
---|---|---|
作者網站 | LYMTICS | https://lymtics.top |
作者 | LYMTICS(樵仙) | https://lymtics.top |
聯絡方式 | [email protected] | [email protected] |
原文標題 | 《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園 | 《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園 |
原文地址 | https://www.cnblogs.com/lymtics/p/16354451.html | https://www.cnblogs.com/lymtics/p/16354451.html |
本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
---|---|---|
作者網站 | LYMTICS | https://lymtics.top |
作者 | LYMTICS(樵仙) | https://lymtics.top |
聯絡方式 | [email protected] | [email protected] |
原文標題 | 《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園 | 《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園 |
原文地址 | https://www.cnblogs.com/lymtics/p/16354451.html | https://www.cnblogs.com/lymtics/p/16354451.html |
Unix 域協定
:
並不是一個實際的協定組,而是在單個主機上執行 C/S 通訊的一種方式
所用的 API 就是之前學習的通訊端 API
使用普通檔案系統中的路徑名標識協定地址
這些路徑名不是普通的 Unix 檔案,除非把它們和 Unix 域通訊端關聯起來,否則無法讀寫這些檔案
Unix 域協定可以被視為 IPC 方法之一
兩類通訊端:
為什麼要使用:
本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
---|---|---|
作者網站 | LYMTICS | https://lymtics.top |
作者 | LYMTICS(樵仙) | https://lymtics.top |
聯絡方式 | [email protected] | [email protected] |
原文標題 | 《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園 | 《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園 |
原文地址 | https://www.cnblogs.com/lymtics/p/16354451.html | https://www.cnblogs.com/lymtics/p/16354451.html |
#include <sys/un.h>
struct sockaddr_un {
sa_family_t sun_familly; // AF_LOCAL
char sun_path[104];
}
sun_path
:
\0
結尾\0
即可,等價於 IPv4 的 INADDR_ANY 以及 IPv6 的 IN6ADDR_ANY_INITsun_family
:
POSIX 為了推廣 Unix 域,而不僅僅是在 Unix 作業系統上使用,把它重新命名為 本地 IPC
,並把 sun_family 由 AF_UNIX
變為 AF_LOCAL
,但是我們仍然使用 Unix 域這個稱呼;
另外,儘管 POSIX 努力使它獨立於作業系統,它的通訊端地址結構仍然保留 _un
字尾
int main(int argc, char** argv) {
int sockfd;
socklen_t len;
struct sockaddr_un addr1, addr2;
if (argc != 2)
err_quit("usage: unixbind <pathname>");
sockfd = Socket(AF_LOCAL, SOCK_STREAM, 0);
// 為了防止已經存在,我們先刪除這個路徑名
// 如果不存在,會返回一個我們將要忽略的錯誤
unlink(argv[1]);
bzero(&addr1, sizeof(addr1));
addr1.sun_family = AF_LOCAL;
// 複製命令列引數,如果過長則會截斷以免路徑名存不下
// 由於 size - 1,所以保證了以 0 結尾
strncpy(addr1.sun_path, argv[1], sizeof(addr1.sun_path) - 1);
// 繫結
// 用 SUN_LEN 計算了 addr1 的長度
Bind(sockfd, (SA*)&addr1, SUN_LEN(&addr1));
len = sizeof(addr2);
Getsockname(sockfd, (SA*)&addr2, &len);
printf("bound name = %s, returned len = %d\n", addr2.sun_path, len);
exit(0);
}
測試:
[root@centos-5610 unixdomain]# ./unixbind /tmp/abc123
bound name = /tmp/abc123, returned len = 14
# 可以多次執行
<div class=anti_spider><div><img src=https://image.lymtics.top/common/logo/default.png loading=lazy></div><div><mark>★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★</mark></div><div class=table-wrapper><table><thead><tr><th>本文資訊<th>本文資訊<th>防爬蟲替換資訊<tbody><tr><td><strong>作者網站</strong><td><a href=https://lymtics.top target=_blank>LYMTICS</a><td><code>https://lymtics.top</code><tr><td><strong>作者</strong><td>LYMTICS(樵仙)<td><code>https://lymtics.top</code><tr><td><strong>聯絡方式</strong><td>[email protected]<td><code>[email protected]</code><tr><td><strong>原文標題</strong><td>《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園<td><code>《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園</code><tr><td><strong>原文地址</strong><td><a href=https://www.cnblogs.com/lymtics/p/16354451.html target=_blank>https://www.cnblogs.com/lymtics/p/16354451.html</a><td><code>https://www.cnblogs.com/lymtics/p/16354451.html</code></table><ul><li>如果您看到了此內容,則本文可能是惡意爬取原作者的文章,建議返回原站閱讀,謝謝您的支援</li><li>原文會不斷地<strong>更新和完善</strong>,<strong>排版和樣式會更加適合閱讀</strong>,並且<strong>有相關配圖</strong></li><li>如果爬蟲破壞了上述連結,可以存取 `lymtics.top` 獲取更多資訊</li></ul><div><mark>★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★</mark></div><div><img src=https://image.lymtics.top/common/logo/default.png loading=lazy></div></div></div>
[root@centos-5610 unixdomain]# ./unixbind /tmp/abc123
bound name = /tmp/abc123, returned len = 14
# 用 ll 觀察一下是否存在該目錄,可以看到型別為 s
<div class=anti_spider><div><img src=https://image.lymtics.top/common/logo/default.png loading=lazy></div><div><mark>★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★</mark></div><div class=table-wrapper><table><thead><tr><th>本文資訊<th>本文資訊<th>防爬蟲替換資訊<tbody><tr><td><strong>作者網站</strong><td><a href=https://lymtics.top target=_blank>LYMTICS</a><td><code>https://lymtics.top</code><tr><td><strong>作者</strong><td>LYMTICS(樵仙)<td><code>https://lymtics.top</code><tr><td><strong>聯絡方式</strong><td>[email protected]<td><code>[email protected]</code><tr><td><strong>原文標題</strong><td>《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園<td><code>《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園</code><tr><td><strong>原文地址</strong><td><a href=https://www.cnblogs.com/lymtics/p/16354451.html target=_blank>https://www.cnblogs.com/lymtics/p/16354451.html</a><td><code>https://www.cnblogs.com/lymtics/p/16354451.html</code></table><ul><li>如果您看到了此內容,則本文可能是惡意爬取原作者的文章,建議返回原站閱讀,謝謝您的支援</li><li>原文會不斷地<strong>更新和完善</strong>,<strong>排版和樣式會更加適合閱讀</strong>,並且<strong>有相關配圖</strong></li><li>如果爬蟲破壞了上述連結,可以存取 `lymtics.top` 獲取更多資訊</li></ul><div><mark>★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★</mark></div><div><img src=https://image.lymtics.top/common/logo/default.png loading=lazy></div></div></div>
[root@centos-5610 unixdomain]# ll /tmp
total 0
srwxr-xr-x. 1 root root 0 Jun 7 02:35 abc123
# 如果名稱過長,會發生截斷
<div class=anti_spider><div><img src=https://image.lymtics.top/common/logo/default.png loading=lazy></div><div><mark>★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★</mark></div><div class=table-wrapper><table><thead><tr><th>本文資訊<th>本文資訊<th>防爬蟲替換資訊<tbody><tr><td><strong>作者網站</strong><td><a href=https://lymtics.top target=_blank>LYMTICS</a><td><code>https://lymtics.top</code><tr><td><strong>作者</strong><td>LYMTICS(樵仙)<td><code>https://lymtics.top</code><tr><td><strong>聯絡方式</strong><td>[email protected]<td><code>[email protected]</code><tr><td><strong>原文標題</strong><td>《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園<td><code>《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園</code><tr><td><strong>原文地址</strong><td><a href=https://www.cnblogs.com/lymtics/p/16354451.html target=_blank>https://www.cnblogs.com/lymtics/p/16354451.html</a><td><code>https://www.cnblogs.com/lymtics/p/16354451.html</code></table><ul><li>如果您看到了此內容,則本文可能是惡意爬取原作者的文章,建議返回原站閱讀,謝謝您的支援</li><li>原文會不斷地<strong>更新和完善</strong>,<strong>排版和樣式會更加適合閱讀</strong>,並且<strong>有相關配圖</strong></li><li>如果爬蟲破壞了上述連結,可以存取 `lymtics.top` 獲取更多資訊</li></ul><div><mark>★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★</mark></div><div><img src=https://image.lymtics.top/common/logo/default.png loading=lazy></div></div></div>
# 這裡最後的長度為 5 + 102 + 1(\0) = 108,很正確
<div class=anti_spider><div><img src=https://image.lymtics.top/common/logo/default.png loading=lazy></div><div><mark>★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★</mark></div><div class=table-wrapper><table><thead><tr><th>本文資訊<th>本文資訊<th>防爬蟲替換資訊<tbody><tr><td><strong>作者網站</strong><td><a href=https://lymtics.top target=_blank>LYMTICS</a><td><code>https://lymtics.top</code><tr><td><strong>作者</strong><td>LYMTICS(樵仙)<td><code>https://lymtics.top</code><tr><td><strong>聯絡方式</strong><td>[email protected]<td><code>[email protected]</code><tr><td><strong>原文標題</strong><td>《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園<td><code>《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園</code><tr><td><strong>原文地址</strong><td><a href=https://www.cnblogs.com/lymtics/p/16354451.html target=_blank>https://www.cnblogs.com/lymtics/p/16354451.html</a><td><code>https://www.cnblogs.com/lymtics/p/16354451.html</code></table><ul><li>如果您看到了此內容,則本文可能是惡意爬取原作者的文章,建議返回原站閱讀,謝謝您的支援</li><li>原文會不斷地<strong>更新和完善</strong>,<strong>排版和樣式會更加適合閱讀</strong>,並且<strong>有相關配圖</strong></li><li>如果爬蟲破壞了上述連結,可以存取 `lymtics.top` 獲取更多資訊</li></ul><div><mark>★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★</mark></div><div><img src=https://image.lymtics.top/common/logo/default.png loading=lazy></div></div></div>
[root@centos-5610 unixdomain]# ./unixbind /tmp/1111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000aaaaaaaaaa
bound name = /tmp/1111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000aa, returned len = 110
問題:
上述程式碼有一個問題,當發生截斷時,再次呼叫會報錯
bind error: Address already in usev
原因是作者的程式碼是先 unlink 再截斷的,所以說 unlink 的是那個長的,而不是截斷後的
關於 umask 的補充說明:
作者在執行上述程式碼前先呼叫了 umask,可以參考:umask 是什麼,下面是一個摘要:
使用者建立的檔案和目錄,都有預設的許可權,比如,檔案的預設許可權為0666,資料夾的預設許可權為0777,因為:
- 建立檔案一般是用來讀寫,所以預設情況下所有使用者都具有讀寫許可權,但是沒有可執行許可權,所以檔案建立的預設許可權為0666
- 而資料夾的 x 許可權表示的是開啟許可權,所以這個許可權必須要有,所以資料夾的預設許可權為0777
但是系統為了保護使用者建立檔案和資料夾的許可權,此時系統會有一個預設的使用者掩碼
umask
,大多數的Linux系統的預設掩碼為022使用者掩碼的作用是使用者在建立檔案時從檔案的預設許可權中去除掩碼中的許可權。所以檔案建立之後的許可權實際為:
預設許可權 - umask
,所以在使用者不修改umask的情況下,建立檔案的許可權為:0666-0022=0644。建立資料夾的許可權為:0777-0022=0755
本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
---|---|---|
作者網站 | LYMTICS | https://lymtics.top |
作者 | LYMTICS(樵仙) | https://lymtics.top |
聯絡方式 | [email protected] | [email protected] |
原文標題 | 《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園 | 《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園 |
原文地址 | https://www.cnblogs.com/lymtics/p/16354451.html | https://www.cnblogs.com/lymtics/p/16354451.html |
socketpair 函數建立兩個隨後連線起來的通訊端。本函數僅適用於 Unix 域通訊端。
#include <sys/socket.h>
int socketpair(int family, // AF_LOCAL
int type, // SOCK_STREAM 或 SOCK_DGRAM
int protocal, // 0
int sockfd[2] // 新建立的通訊端描述符作為 sockfd[0] 和 sockfd[1] 返回
);
這樣建立的兩個通訊端不曾命名,也就是說其中沒有設計隱式的 bind 呼叫
指定 type 為 SOCK_STREAM 得到的結果稱為流管道,它與呼叫 pipe 建立的普通 Unix 管道類似,差別在於流管道是全雙工的,即兩個描述符都是既可讀又可寫。
本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
---|---|---|
作者網站 | LYMTICS | https://lymtics.top |
作者 | LYMTICS(樵仙) | https://lymtics.top |
聯絡方式 | [email protected] | [email protected] |
原文標題 | 《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園 | 《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園 |
原文地址 | https://www.cnblogs.com/lymtics/p/16354451.html | https://www.cnblogs.com/lymtics/p/16354451.html |
當用於 Unix 域通訊端時,通訊端函數中存在一些差異和限制:
由 bind 建立的路徑名預設存取許可權應該是 0777,並按照當前 umask 值進行修正
與 Unix 域通訊端關聯的路徑名應該是一個絕對路徑名,而不是一個相對路徑名
在 connect 呼叫中指定的路徑名必須是一個當前繫結在某個開啟的 Unix 域通訊端上的路徑名,而且它們的通訊端型別也必須一致
如果該路徑不是 Unix 域通訊端,或沒有與之關聯的開啟的描述符,或型別不符,就會報錯
呼叫 connect 連線一個 Unix 域通訊端涉及的許可權等同於呼叫 open 以只寫方式存取相應的路徑名
Unix 域位元組流通訊端類似 TCP 通訊端:它們都提供無記錄邊界的位元組流介面
如果對於某個 Unix 域位元組流通訊端的 connect 呼叫發現這個監聽通訊端的佇列已滿,呼叫就立即返回一個 E.CONN.REFUSED 錯誤,這一點不同於 TCP:伺服器端忽略,使用者端重傳
Unix 域資料包通訊端類似 UDP 通訊端:它們都提供一個保留記錄邊界的不可靠的資料包服務
在一個未繫結的 Unix 域通訊端上傳送資料包不會自動給這個通訊端捆綁一個路徑名,這一點和 UDP 不同;這意味著除非資料傳送端已經繫結一個路徑名到它的通訊端,否則資料包接收端無法發回應答資料包;類似地,對於某個 Unix 域資料包通訊端的 connect 呼叫不會給本通訊端捆綁一個路徑名,這一點不同於 TCP 和 UDP
本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
---|---|---|
作者網站 | LYMTICS | https://lymtics.top |
作者 | LYMTICS(樵仙) | https://lymtics.top |
聯絡方式 | [email protected] | [email protected] |
原文標題 | 《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園 | 《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園 |
原文地址 | https://www.cnblogs.com/lymtics/p/16354451.html | https://www.cnblogs.com/lymtics/p/16354451.html |
伺服器程式就像整合了我們之前的 TCP 使用者端和剛剛說的 Unix 域通訊端操作:
int main(int argc, char** argv) {
int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
// sockaddr_un 而不是 sockaddr,下同
struct sockaddr_un cliaddr, servaddr;
void sig_chld(int);
// AF_LOCAL 指明建立 Unix 域通訊端
listenfd = Socket(AF_LOCAL, SOCK_STREAM, 0);
// UNIXSTR_PATH:預先定義好的路徑常數
unlink(UNIXSTR_PATH);
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_LOCAL;
strcpy(servaddr.sun_path, UNIXSTR_PATH);
Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
Signal(SIGCHLD, sig_chld);
for (;;) {
clilen = sizeof(cliaddr);
if ((connfd = accept(listenfd, (SA*)&cliaddr, &clilen)) < 0) {
if (errno == EINTR)
continue; /* back to for() */
else
err_sys("accept error");
}
if ((childpid = Fork()) == 0) { /* child process */
Close(listenfd); /* close listening socket */
str_echo(connfd); /* process request */
exit(0);
}
Close(connfd); /* parent closes connected socket */
}
}
int main(int argc, char** argv) {
int sockfd;
struct sockaddr_un servaddr;
sockfd = Socket(AF_LOCAL, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_LOCAL;
strcpy(servaddr.sun_path, UNIXSTR_PATH);
Connect(sockfd, (SA*)&servaddr, sizeof(servaddr));
str_cli(stdin, sockfd); /* do it all */
exit(0);
}
本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
---|---|---|
作者網站 | LYMTICS | https://lymtics.top |
作者 | LYMTICS(樵仙) | https://lymtics.top |
聯絡方式 | [email protected] | [email protected] |
原文標題 | 《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園 | 《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園 |
原文地址 | https://www.cnblogs.com/lymtics/p/16354451.html | https://www.cnblogs.com/lymtics/p/16354451.html |
int main(int argc, char** argv) {
int sockfd;
struct sockaddr_un servaddr, cliaddr;
sockfd = Socket(AF_LOCAL, SOCK_DGRAM, 0);
unlink(UNIXDG_PATH);
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_LOCAL;
strcpy(servaddr.sun_path, UNIXDG_PATH);
Bind(sockfd, (SA*)&servaddr, sizeof(servaddr));
dg_echo(sockfd, (SA*)&cliaddr, sizeof(cliaddr));
}
int main(int argc, char** argv) {
int sockfd;
struct sockaddr_un cliaddr, servaddr;
sockfd = Socket(AF_LOCAL, SOCK_DGRAM, 0);
bzero(&cliaddr, sizeof(cliaddr)); /* bind an address for us */
cliaddr.sun_family = AF_LOCAL;
strcpy(cliaddr.sun_path, tmpnam(NULL));
// 要手動 Bind !! UDP 則不用
Bind(sockfd, (SA*)&cliaddr, sizeof(cliaddr));
bzero(&servaddr, sizeof(servaddr)); /* fill in server's address */
servaddr.sun_family = AF_LOCAL;
strcpy(servaddr.sun_path, UNIXDG_PATH);
// 實際的傳送在這個函數裡
dg_cli(stdin, sockfd, (SA*)&servaddr, sizeof(servaddr));
exit(0);
}
如果沒有 Bind,那麼伺服器在 dg_echo 函數中的 recvfrom 呼叫將返回一個空路徑名,這個空路徑名將導致伺服器在呼叫 sendto 時發生錯誤:
[root@centos-5610 unixdomain]# ./unixdgserv01
sendto error: Transport endpoint is not connected
本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
---|---|---|
作者網站 | LYMTICS | https://lymtics.top |
作者 | LYMTICS(樵仙) | https://lymtics.top |
聯絡方式 | [email protected] | [email protected] |
原文標題 | 《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園 | 《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園 |
原文地址 | https://www.cnblogs.com/lymtics/p/16354451.html | https://www.cnblogs.com/lymtics/p/16354451.html |
之前我們可以讓父程序把描述符傳遞給子程序:
使用 Unix 域通訊端:
具體步驟如下:
建立一個 Unix 域通訊端
- 如果是子程序向父程序傳遞,父程序可以預先呼叫 socketpair 建立一個可用於在父子程序間交換描述符的流管道
- 如果沒有親緣關係,則父程序必須建立一個 Unix 域通訊端,bind 一個路徑名到該通訊端以允許客戶程序 connect 到該通訊端;然後客戶可以向伺服器傳送一個開啟某個描述符的請求,伺服器再把該描述符通過 Unix 域通訊端傳遞迴客戶
傳送程序開啟一個描述符
可以用 Unix 函數如:open、pipe、mkfifo、socket、accept 等建立一個描述符(而不只是檔案描述符)
傳送程序建立一個 msghdr 結構,將待傳遞的描述符作為輔助資料(msg_control)傳送
傳送描述符會使描述符的參照計數增加1(回憶之前的 fork),因此,即使傳送程序在呼叫 sendmsg 之後但在接受程序呼叫 recvmsg 之前關閉了該描述符,對於接受程序而言它仍然保持開啟狀態。我們說這個描述符「在飛行中」(in flight)。
接受程序呼叫 recvmsg 接收這個描述符,並可能會分配一個新的描述符數位
目標:
圖示:
程式碼:
mycat.c
int my_open(const char*, int);
int main(int argc, char** argv) {
int fd, n;
char buff[BUFFSIZE];
if (argc != 2)
err_quit("usage: mycat <pathname>");
// 呼叫 my_open 開啟檔案
// 如果改成 open,則是簡單地開啟一個檔案
if ((fd = my_open(argv[1], O_RDONLY)) < 0)
err_sys("cannot open %s", argv[1]);
while ((n = Read(fd, buff, BUFFSIZE)) > 0)
Write(STDOUT_FILENO, buff, n);
exit(0);
}
myopen.c
int my_open(const char* pathname, int mode) {
int fd, sockfd[2], status;
pid_t childpid;
char c, argsockfd[10], argmode[10];
// 建立一個流管道,返回兩個描述符儲存在 sockfd 中
Socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);
if ((childpid = Fork()) == 0) { /* child process */
// 關閉 0 ,使用 1
Close(sockfd[0]);
// int snprintf(char *str, size_t size, const char *format, ...)
// 將可變引數(...)按照 format 格式化成字串,並將字串複製到 str
// size 為要寫入的字元的最大數目,超過 size 會被截斷。
snprintf(argsockfd, sizeof(argsockfd), "%d", sockfd[1]);
snprintf(argmode, sizeof(argmode), "%d", mode);
// exec 的一種變型,之前提到過
execl("./openfile", "openfile", argsockfd, pathname, argmode,
(char*)NULL);
err_sys("execl error");
}
// 如下為父程序的操作
// 關閉 1,流下 0
Close(sockfd[1]); /* close the end we don't use */
// 等待子程序終止 waitpid 的知識前面提到過
Waitpid(childpid, &status, 0);
if (WIFEXITED(status) == 0)
err_quit("child did not terminate");
// 用 W.EXIT.STATUS 把終止狀態轉換成退出狀態
if ((status = WEXITSTATUS(status)) == 0)
// 我們自己寫的函數,他將通過流管道接收描述符
// 除了描述符外,我們還讀取了一個位元組的資料,但是不對其進行任何處理
Read_fd(sockfd[0], &c, 1, &fd);
else {
errno = status; /* set errno value from child's status */
fd = -1;
}
Close(sockfd[0]);
return (fd);
read_fd.c
ssize_t read_fd(int fd, void* ptr, size_t nbytes, int* recvfd) {
struct msghdr msg;
struct iovec iov[1];
ssize_t n;
// 本函數必須處理兩個版本的 recvmsg:使用 msg_control 或 msg_accrights
// 前者會定義常數 HAVE_MSGHDR_MSG_CONTROL
#ifdef HAVE_MSGHDR_MSG_CONTROL
// msg_control 緩衝區必須為 cmsghdr 結構適當地對齊,所以宣告這個聯合
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr* cmptr;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
#else
int newfd;
msg.msg_accrights = (caddr_t)&newfd;
msg.msg_accrightslen = sizeof(int);
#endif
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = ptr;
iov[0].iov_len = nbytes;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
// 接收資料
if ((n = recvmsg(fd, &msg, 0)) <= 0)
return (n);
#ifdef HAVE_MSGHDR_MSG_CONTROL
// 對輔助資料進行格式驗證
if ((cmptr = CMSG_FIRSTHDR(&msg)) != NULL &&
cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
if (cmptr->cmsg_level != SOL_SOCKET)
err_quit("control level != SOL_SOCKET");
if (cmptr->cmsg_type != SCM_RIGHTS)
err_quit("control type != SCM_RIGHTS");
// 通過驗證,取出資料
*recvfd = *((int*)CMSG_DATA(cmptr));
} else
*recvfd = -1; /* descriptor was not passed */
#else
/* *INDENT-OFF* */
if (msg.msg_accrightslen == sizeof(int))
*recvfd = newfd;
else
*recvfd = -1; /* descriptor was not passed */
/* *INDENT-ON* */
#endif
return (n);
}
/* end read_fd */
ssize_t Read_fd(int fd, void* ptr, size_t nbytes, int* recvfd) {
ssize_t n;
if ((n = read_fd(fd, ptr, nbytes, recvfd)) < 0)
err_sys("read_fd error");
return (n);
}
openfile.c
int main(int argc, char** argv) {
int fd;
if (argc != 4)
err_quit("openfile <sockfd#> <filename> <mode>");
if ((fd = open(argv[2], atoi(argv[3]))) < 0)
exit((errno > 0) ? errno : 255);
if (write_fd(atoi(argv[1]), "", 1, fd) < 0)
exit((errno > 0) ? errno : 255);
exit(0);
}
write_fd.c
ssize_t write_fd(int fd, void* ptr, size_t nbytes, int sendfd) {
struct msghdr msg;
struct iovec iov[1];
#ifdef HAVE_MSGHDR_MSG_CONTROL
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr* cmptr;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
cmptr = CMSG_FIRSTHDR(&msg);
cmptr->cmsg_len = CMSG_LEN(sizeof(int));
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
*((int*)CMSG_DATA(cmptr)) = sendfd;
#else
msg.msg_accrights = (caddr_t)&sendfd;
msg.msg_accrightslen = sizeof(int);
#endif
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = ptr;
iov[0].iov_len = nbytes;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
// 傳送
return (sendmsg(fd, &msg, 0));
}
/* end write_fd */
ssize_t Write_fd(int fd, void* ptr, size_t nbytes, int sendfd) {
ssize_t n;
if ((n = write_fd(fd, ptr, nbytes, sendfd)) < 0)
err_sys("write_fd error");
return (n);
}
本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
---|---|---|
作者網站 | LYMTICS | https://lymtics.top |
作者 | LYMTICS(樵仙) | https://lymtics.top |
聯絡方式 | [email protected] | [email protected] |
原文標題 | 《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園 | 《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園 |
原文地址 | https://www.cnblogs.com/lymtics/p/16354451.html | https://www.cnblogs.com/lymtics/p/16354451.html |
憑證傳遞仍然是一個尚未普及且無統一規範的特性,然而它是對 Unix 域協定的一個儘管簡單卻也重要的補充。
當客戶和伺服器進行通訊時,伺服器通常需要以一定的手段獲悉客戶的身份,以便驗證客戶是否有許可權請求相應的服務。
// FreeBSD
#include <sys/socket.h>
struct cmsgcred {
pid_t cmcred_pid; // PID
uid_t cmcred_uid; // real UID
uid_t cmcred_euid; // effective UID
gid_t cmcred_gid; // read GID of sending process
short cmcred_ngroups; // 組 數量
gid_t cmcred_groups[CMGROUP_MAX]; // 組
}
憑證資訊總是可以通過 Unix 域通訊端在兩個程序之間傳遞,然而傳送程序在傳送它們時往往需要做特殊的封裝處理,接收程式接收它們時也往往需要特殊的接受處理。
例如,在 FreeBSD 系統中,接收程序只需在呼叫 recvmsg 同時提供一個足以存放憑證的輔助資料空間即可,而傳送程序呼叫 sendmsg 時必須作為輔助資料包含一個 cmsgcred 結構才會隨資料傳遞憑證。
需要注意的是,cmsgcred 結構雖然是使用者提供的,但是其內容卻是核心填寫的,傳送程序無法偽造,總而保證了通過 Unix 域通訊端傳遞憑證來驗證使用者身份的可靠性。
目的:
strecho.c
,使其在應答前先用 read_cred
函數獲取使用者的憑證資訊read_cred 程式碼如下:
readcred.c
#define CONTROL_LEN (sizeof(struct cmsghdr) + sizeof(struct cmsgcred))
ssize_t read_cred(int fd,
void* ptr,
size_t nbytes,
struct cmsgcred* cmsgcredptr // 憑證
) {
struct msghdr msg;
struct iovec iov[1];
char control[CONTROL_LEN];
int n;
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = ptr;
iov[0].iov_len = nbytes;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
msg.msg_flags = 0;
if ((n = recvmsg(fd, &msg, 0)) < 0)
return (n);
cmsgcredptr->cmcred_ngroups = 0; /* indicates no credentials returned */
// 如果有憑證返回
if (cmsgcredptr && msg.msg_controllen > 0) {
struct cmsghdr* cmptr = (struct cmsghdr*)control;
// 對其長度、等級、型別進行驗證
if (cmptr->cmsg_len < CONTROL_LEN)
err_quit("control length = %d", cmptr->cmsg_len);
if (cmptr->cmsg_level != SOL_SOCKET)
err_quit("control level != SOL_SOCKET");
if (cmptr->cmsg_type != SCM_CREDS)
err_quit("control type != SCM_CREDS");
// 通過驗證,則複製到 cmsgcred 結構中
memcpy(cmsgcredptr, CMSG_DATA(cmptr), sizeof(struct cmsgcred));
}
return (n);
}
strecho.c
改寫如下:
ssize_t read_cred(int, void*, size_t, struct cmsgcred*);
void str_echo(int sockfd) {
ssize_t n;
int i;
char buf[MAXLINE];
struct cmsgcred cred;
again:
while ((n = read_cred(sockfd, buf, MAXLINE, &cred)) > 0) {
if (cred.cmcred_ngroups == 0) {
printf("(no credentials returned)\n");
} else {
printf("PID of sender = %d\n", cred.cmcred_pid);
printf("real user ID = %d\n", cred.cmcred_uid);
printf("real group ID = %d\n", cred.cmcred_gid);
printf("effective user ID = %d\n", cred.cmcred_euid);
printf("%d groups:", cred.cmcred_ngroups - 1);
for (i = 1; i < cred.cmcred_ngroups; i++)
printf(" %d", cred.cmcred_groups[i]);
printf("\n");
}
Writen(sockfd, buf, n);
}
if (n < 0 && errno == EINTR)
goto again;
else if (n < 0)
err_sys("str_echo: read error");
}
可以用
id
如下命令檢視個人的當前憑證:[root@centos-5610 unixdomain]# id uid=0(root) gid=0(root) groups=0(root) ...