前の節で説明した PORTMAPPER サービスは、UDP を使用して実装されます。TCP と異なり、UDP ソケットは接続指向ではないので、マルチレベルポートでクライアントに応答する場合にどの資格を使用するかに関してあいまいになることがあります。したがって、クライアントの要求ソケットは、サーバーの応答ソケットに明示的に関連付けられなければなりません。これを関連付けるには、SO_RECVUCRED ソケットオプションを使用します。
SO_RECVUCRED が UDP ソケットで設定されると、カーネル UDP モジュールは ucred 構造体のラベルを補助データとしてアプリケーションに渡します。ucred の level と type の値がそれぞれ、SOL_SOCKET と SCM_UCRED です。
アプリケーションは次のいずれかの方法でこの ucred 構造体を処理します。
この ucred 構造体を受信バッファーから送信バッファーにコピーする
受信バッファーを送信バッファーとして再利用し、受信バッファーの ucred 構造体をそのままにする
次のコード抜粋は再利用の場合を示します。
/* * src で SCM_UCRED を検索し、dest のみのオプションに * ポインタを指定する。これら 2 つの 'netbuf' 構造体が同じ * である場合もある。そのため、コードで dest の変更後に * src を照会するときに注意が必要。 */ static void extract_cred(const struct netbuf *src, struct netbuf *dest) { char *cp = src->buf; unsigned int len = src->len; const struct T_opthdr *opt; unsigned int olen; while (len >= sizeof (*opt)) { /* LINTED: pointer alignment */ opt = (const struct T_opthdr *)cp; olen = opt->len; if (olen > len || olen < sizeof (*opt) || !IS_P2ALIGNED(olen, sizeof (t_uscalar_t))) break; if (opt->level == SOL_SOCKET && opt->name == SCM_UCRED) { dest->buf = cp; dest->len = olen; return; } cp += olen; len -= olen; } dest->len = 0; }
次のコード抜粋は、受信バッファーからユーザー資格にアクセスする方法を示します。
void examine_udp_label() { struct msghdr recv_msg; struct cmsghdr *cmsgp; char message[MAX_MSGLEN+1]; char inmsg[MAX_MSGLEN+1]; int on = 1; setsockopt(sockfd, SOL_SOCKET, SO_RECVUCRED, (void *)&on, sizeof (int)); [...] while (1) { if (recvmsg(sockfd, &recv_msg, 0) < 0) { (void) fprintf(stderr, "recvmsg_errno: %d\n", errno); exit(1); } /* * 補助データで ucred を検査する */ ucred = NULL; for (cmsgp = CMSG_FIRSTHDR(&recv_msg); cmsgp; cmsgp = CMSG_NXTHDR(&recv_msg, cmsgp)) { if (cmsgp->cmsg_level == SOL_SOCKET && cmsgp->cmsg_type == SCM_UCRED) { ucred = (ucred_t *)CMSG_DATA(cmsgp); break; } if (ucred == NULL) { (void) sprintf(&message[0], "No ucred info in ancillary data with UDP"); } else { /* * ここで、ucred_getlabel(3C) を使用して、ucred から * ラベルを抽出することもできる。 */ } } [...] if (message != NULL) (void) strlcpy(&inmsg[0], message, MAX_MSGLEN); /* * 正しいラベルが含まれるように、受信したメッセージを * 使用する */ iov.iov_len = strlen(inmsg); ret = sendmsg(sockfd, &recv_msg, 0); } }