《Unix 網路程式設計》15:Unix 域協定

2022-06-08 12:01:51

Unix 域協定

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文資訊本文資訊防爬蟲替換資訊
作者網站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
聯絡方式[email protected][email protected]
原文標題《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園
原文地址https://www.cnblogs.com/lymtics/p/16354451.htmlhttps://www.cnblogs.com/lymtics/p/16354451.html
  • 如果您看到了此內容,則本文可能是惡意爬取原作者的文章,建議返回原站閱讀,謝謝您的支援
  • 原文會不斷地更新和完善排版和樣式會更加適合閱讀,並且有相關配圖
  • 如果爬蟲破壞了上述連結,可以存取 `lymtics.top` 獲取更多資訊
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

介紹

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文資訊本文資訊防爬蟲替換資訊
作者網站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
聯絡方式[email protected][email protected]
原文標題《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園
原文地址https://www.cnblogs.com/lymtics/p/16354451.htmlhttps://www.cnblogs.com/lymtics/p/16354451.html
  • 如果您看到了此內容,則本文可能是惡意爬取原作者的文章,建議返回原站閱讀,謝謝您的支援
  • 原文會不斷地更新和完善排版和樣式會更加適合閱讀,並且有相關配圖
  • 如果爬蟲破壞了上述連結,可以存取 `lymtics.top` 獲取更多資訊
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

Unix 域協定

  • 並不是一個實際的協定組,而是在單個主機上執行 C/S 通訊的一種方式

  • 所用的 API 就是之前學習的通訊端 API

  • 使用普通檔案系統中的路徑名標識協定地址

    這些路徑名不是普通的 Unix 檔案,除非把它們和 Unix 域通訊端關聯起來,否則無法讀寫這些檔案

  • Unix 域協定可以被視為 IPC 方法之一

兩類通訊端:

  • 位元組流通訊端(類似 TCP)
  • 資料包通訊端(類似 UDP)

為什麼要使用:

  1. :比位於同一主機的 TCP 快出一倍多
  2. 可以用在同一主機不同程序間傳遞描述符
  3. 把客戶的憑證提供給伺服器,從而可以提供額外的安全檢查措施

Unix 域通訊端結構

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文資訊本文資訊防爬蟲替換資訊
作者網站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
聯絡方式[email protected][email protected]
原文標題《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園
原文地址https://www.cnblogs.com/lymtics/p/16354451.htmlhttps://www.cnblogs.com/lymtics/p/16354451.html
  • 如果您看到了此內容,則本文可能是惡意爬取原作者的文章,建議返回原站閱讀,謝謝您的支援
  • 原文會不斷地更新和完善排版和樣式會更加適合閱讀,並且有相關配圖
  • 如果爬蟲破壞了上述連結,可以存取 `lymtics.top` 獲取更多資訊
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

結構說明

#include <sys/un.h>

struct sockaddr_un {
  sa_family_t sun_familly; // AF_LOCAL
  char sun_path[104];
}

sun_path

  1. 可見路徑的長度是有限制的,不同系統的長度可能不一致,可能在 92 ~ 108 之間
  2. 應該以 \0 結尾
  3. 如果沒有指定地址,則只儲存一個 \0 即可,等價於 IPv4 的 INADDR_ANY 以及 IPv6 的 IN6ADDR_ANY_INIT

sun_family

POSIX 為了推廣 Unix 域,而不僅僅是在 Unix 作業系統上使用,把它重新命名為 本地 IPC ,並把 sun_family 由 AF_UNIX 變為 AF_LOCAL ,但是我們仍然使用 Unix 域這個稱呼;

另外,儘管 POSIX 努力使它獨立於作業系統,它的通訊端地址結構仍然保留 _un 字尾

案例:bind 呼叫

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

socketpair 函數

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文資訊本文資訊防爬蟲替換資訊
作者網站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
聯絡方式[email protected][email protected]
原文標題《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園
原文地址https://www.cnblogs.com/lymtics/p/16354451.htmlhttps://www.cnblogs.com/lymtics/p/16354451.html
  • 如果您看到了此內容,則本文可能是惡意爬取原作者的文章,建議返回原站閱讀,謝謝您的支援
  • 原文會不斷地更新和完善排版和樣式會更加適合閱讀,並且有相關配圖
  • 如果爬蟲破壞了上述連結,可以存取 `lymtics.top` 獲取更多資訊
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

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 管道類似,差別在於流管道是全雙工的,即兩個描述符都是既可讀又可寫。

通訊端函數的差別

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文資訊本文資訊防爬蟲替換資訊
作者網站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
聯絡方式[email protected][email protected]
原文標題《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園
原文地址https://www.cnblogs.com/lymtics/p/16354451.htmlhttps://www.cnblogs.com/lymtics/p/16354451.html
  • 如果您看到了此內容,則本文可能是惡意爬取原作者的文章,建議返回原站閱讀,謝謝您的支援
  • 原文會不斷地更新和完善排版和樣式會更加適合閱讀,並且有相關配圖
  • 如果爬蟲破壞了上述連結,可以存取 `lymtics.top` 獲取更多資訊
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

當用於 Unix 域通訊端時,通訊端函數中存在一些差異和限制:

  1. 由 bind 建立的路徑名預設存取許可權應該是 0777,並按照當前 umask 值進行修正

  2. 與 Unix 域通訊端關聯的路徑名應該是一個絕對路徑名,而不是一個相對路徑名

  3. 在 connect 呼叫中指定的路徑名必須是一個當前繫結在某個開啟的 Unix 域通訊端上的路徑名,而且它們的通訊端型別也必須一致

    如果該路徑不是 Unix 域通訊端,或沒有與之關聯的開啟的描述符,或型別不符,就會報錯

  4. 呼叫 connect 連線一個 Unix 域通訊端涉及的許可權等同於呼叫 open 以只寫方式存取相應的路徑名

  5. Unix 域位元組流通訊端類似 TCP 通訊端:它們都提供無記錄邊界的位元組流介面

  6. 如果對於某個 Unix 域位元組流通訊端的 connect 呼叫發現這個監聽通訊端的佇列已滿,呼叫就立即返回一個 E.CONN.REFUSED 錯誤,這一點不同於 TCP:伺服器端忽略,使用者端重傳

  7. Unix 域資料包通訊端類似 UDP 通訊端:它們都提供一個保留記錄邊界的不可靠的資料包服務

  8. 在一個未繫結的 Unix 域通訊端上傳送資料包不會自動給這個通訊端捆綁一個路徑名,這一點和 UDP 不同;這意味著除非資料傳送端已經繫結一個路徑名到它的通訊端,否則資料包接收端無法發回應答資料包;類似地,對於某個 Unix 域資料包通訊端的 connect 呼叫不會給本通訊端捆綁一個路徑名,這一點不同於 TCP 和 UDP

位元組流程式

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文資訊本文資訊防爬蟲替換資訊
作者網站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
聯絡方式[email protected][email protected]
原文標題《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園
原文地址https://www.cnblogs.com/lymtics/p/16354451.htmlhttps://www.cnblogs.com/lymtics/p/16354451.html
  • 如果您看到了此內容,則本文可能是惡意爬取原作者的文章,建議返回原站閱讀,謝謝您的支援
  • 原文會不斷地更新和完善排版和樣式會更加適合閱讀,並且有相關配圖
  • 如果爬蟲破壞了上述連結,可以存取 `lymtics.top` 獲取更多資訊
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

伺服器端

伺服器程式就像整合了我們之前的 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);
}

資料包程式

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文資訊本文資訊防爬蟲替換資訊
作者網站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
聯絡方式[email protected][email protected]
原文標題《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園
原文地址https://www.cnblogs.com/lymtics/p/16354451.htmlhttps://www.cnblogs.com/lymtics/p/16354451.html
  • 如果您看到了此內容,則本文可能是惡意爬取原作者的文章,建議返回原站閱讀,謝謝您的支援
  • 原文會不斷地更新和完善排版和樣式會更加適合閱讀,並且有相關配圖
  • 如果爬蟲破壞了上述連結,可以存取 `lymtics.top` 獲取更多資訊
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

伺服器端

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

描述符傳遞

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文資訊本文資訊防爬蟲替換資訊
作者網站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
聯絡方式[email protected][email protected]
原文標題《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園
原文地址https://www.cnblogs.com/lymtics/p/16354451.htmlhttps://www.cnblogs.com/lymtics/p/16354451.html
  • 如果您看到了此內容,則本文可能是惡意爬取原作者的文章,建議返回原站閱讀,謝謝您的支援
  • 原文會不斷地更新和完善排版和樣式會更加適合閱讀,並且有相關配圖
  • 如果爬蟲破壞了上述連結,可以存取 `lymtics.top` 獲取更多資訊
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

之前的方法

之前我們可以讓父程序把描述符傳遞給子程序:

  • fork 呼叫返回之後,子程序共用父程序所有開啟的描述符
  • exec 呼叫執行之後,所有描述符通常保持開啟狀態不變

本章的方法

使用 Unix 域通訊端:

  • 在兩個程序之間建立一個 Unix 域通訊端,然後使用 sendmsg 跨這個通訊端傳送一個特殊訊息
  • 這個訊息由核心來專門處理,會把開啟的描述符從傳送程序傳遞到接受程序

具體步驟如下:

  1. 建立一個 Unix 域通訊端

    • 如果是子程序向父程序傳遞,父程序可以預先呼叫 socketpair 建立一個可用於在父子程序間交換描述符的流管道
    • 如果沒有親緣關係,則父程序必須建立一個 Unix 域通訊端,bind 一個路徑名到該通訊端以允許客戶程序 connect 到該通訊端;然後客戶可以向伺服器傳送一個開啟某個描述符的請求,伺服器再把該描述符通過 Unix 域通訊端傳遞迴客戶
  2. 傳送程序開啟一個描述符

    可以用 Unix 函數如:open、pipe、mkfifo、socket、accept 等建立一個描述符(而不只是檔案描述符)

  3. 傳送程序建立一個 msghdr 結構,將待傳遞的描述符作為輔助資料(msg_control)傳送

    傳送描述符會使描述符的參照計數增加1(回憶之前的 fork),因此,即使傳送程序在呼叫 sendmsg 之後但在接受程序呼叫 recvmsg 之前關閉了該描述符,對於接受程序而言它仍然保持開啟狀態。我們說這個描述符「在飛行中」(in flight)。

  4. 接受程序呼叫 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);
}

接收傳送者的憑證

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文資訊本文資訊防爬蟲替換資訊
作者網站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
聯絡方式[email protected][email protected]
原文標題《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園《Unix 網路程式設計》15:Unix 域協定 - 樵仙 - 部落格園
原文地址https://www.cnblogs.com/lymtics/p/16354451.htmlhttps://www.cnblogs.com/lymtics/p/16354451.html
  • 如果您看到了此內容,則本文可能是惡意爬取原作者的文章,建議返回原站閱讀,謝謝您的支援
  • 原文會不斷地更新和完善排版和樣式會更加適合閱讀,並且有相關配圖
  • 如果爬蟲破壞了上述連結,可以存取 `lymtics.top` 獲取更多資訊
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

介紹

憑證傳遞仍然是一個尚未普及且無統一規範的特性,然而它是對 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 域通訊端傳遞憑證來驗證使用者身份的可靠性。

案例

目的:

  • 改寫之前那個 Unix 域通訊端回射程式
  • 讓使用者端在用 sendmsg 傳送訊息時額外攜帶 一個空的 cmsgcred 結構
  • 修改 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) ...