diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h index 7d952c7..f9b28ec 100644 --- a/modules/include/kc_socket.h +++ b/modules/include/kc_socket.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -42,15 +43,13 @@ { /** - * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * ソケットの接続先アドレスを返します。 + * アドレスが取得できない場合(未接続状態など)、NULL を返します。 * * @param sock 対象ソケット - * @param addr 接続先アドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットの接続先アドレス */ - const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + const char *(*get_remote_addr)(struct KcSocket_ *sock); /** * ソケットの接続先ポート番号を返します。 @@ -62,15 +61,13 @@ int (*get_remote_port)(struct KcSocket_ *sock); /** - * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * ソケットのローカルアドレスを返します。 + * アドレスが取得できない場合、NULL を返します。 * * @param sock 対象ソケット - * @param addr ローカルアドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットのローカルアドレス */ - const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + const char *(*get_local_addr)(struct KcSocket_ *sock); /** * ソケットのローカルポート番号を返します。 @@ -227,7 +224,7 @@ * @param src_addrlen 送信元アドレス格納用バッファサイズ * @param src_service 送信元サービス格納用バッファ * @param src_servicelen 送信元サービス格納用バッファサイズ - * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + * @return 受信メッセージサイズ、エラー発生時は -1 */ ssize_t (*recvfrom)( struct KcSocket_ *sock, @@ -246,23 +243,45 @@ /** * マルチキャストグループに参加するために JOIN します。 + * IPv4 の場合、ifname に、IPアドレスを指定ください。 + * IPv6 の場合、ifname にインタフェース名 (例: eth0 など)を指定ください。 + * ifname に NULL が指定された場合、任意のインタフェースとなります。 * * @param sock 対象ソケット * @param addr マルチキャストアドレス * @param ifname インタフェース名 - * @param ifindex インタフェースインデックス * @return true/false (成功/失敗) */ - bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname); /** * マルチキャストグループから離脱します。 * * @param sock 対象ソケット * @param addr マルチキャストアドレス + * @param ifname インタフェース名 * @return true/false (成功/失敗) */ - bool (*leave)(struct KcSocket_ *sock, const char *addr); + bool (*leave)(struct KcSocket_ *sock, const char *addr, const char *ifname); + + /** + * マルチキャストを送信するインタフェースを指定します。 + * + * @param sock 対象ソケット + * @param v4_ifname インタフェース名(IPアドレス) + * @param v6_ifname インタフェース名(eth0 など) + * @return true/false (成功/失敗) + */ + void (*set_ifname)(struct KcSocket_ *sock, const char *v4_ifname, const char *v6_ifname); + + /** + * 指定されたソケットの情報を指定されたバッファに出力します。 + * + * @param buff バッファ + * @param size バッファサイズ + * @param sock 対象ソケット + */ + void (*print_info)(struct KcSocket_ *sock, char *buff, size_t size); /** * Socket 管理情報 diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h index 7d952c7..f9b28ec 100644 --- a/modules/include/kc_socket.h +++ b/modules/include/kc_socket.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -42,15 +43,13 @@ { /** - * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * ソケットの接続先アドレスを返します。 + * アドレスが取得できない場合(未接続状態など)、NULL を返します。 * * @param sock 対象ソケット - * @param addr 接続先アドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットの接続先アドレス */ - const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + const char *(*get_remote_addr)(struct KcSocket_ *sock); /** * ソケットの接続先ポート番号を返します。 @@ -62,15 +61,13 @@ int (*get_remote_port)(struct KcSocket_ *sock); /** - * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * ソケットのローカルアドレスを返します。 + * アドレスが取得できない場合、NULL を返します。 * * @param sock 対象ソケット - * @param addr ローカルアドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットのローカルアドレス */ - const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + const char *(*get_local_addr)(struct KcSocket_ *sock); /** * ソケットのローカルポート番号を返します。 @@ -227,7 +224,7 @@ * @param src_addrlen 送信元アドレス格納用バッファサイズ * @param src_service 送信元サービス格納用バッファ * @param src_servicelen 送信元サービス格納用バッファサイズ - * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + * @return 受信メッセージサイズ、エラー発生時は -1 */ ssize_t (*recvfrom)( struct KcSocket_ *sock, @@ -246,23 +243,45 @@ /** * マルチキャストグループに参加するために JOIN します。 + * IPv4 の場合、ifname に、IPアドレスを指定ください。 + * IPv6 の場合、ifname にインタフェース名 (例: eth0 など)を指定ください。 + * ifname に NULL が指定された場合、任意のインタフェースとなります。 * * @param sock 対象ソケット * @param addr マルチキャストアドレス * @param ifname インタフェース名 - * @param ifindex インタフェースインデックス * @return true/false (成功/失敗) */ - bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname); /** * マルチキャストグループから離脱します。 * * @param sock 対象ソケット * @param addr マルチキャストアドレス + * @param ifname インタフェース名 * @return true/false (成功/失敗) */ - bool (*leave)(struct KcSocket_ *sock, const char *addr); + bool (*leave)(struct KcSocket_ *sock, const char *addr, const char *ifname); + + /** + * マルチキャストを送信するインタフェースを指定します。 + * + * @param sock 対象ソケット + * @param v4_ifname インタフェース名(IPアドレス) + * @param v6_ifname インタフェース名(eth0 など) + * @return true/false (成功/失敗) + */ + void (*set_ifname)(struct KcSocket_ *sock, const char *v4_ifname, const char *v6_ifname); + + /** + * 指定されたソケットの情報を指定されたバッファに出力します。 + * + * @param buff バッファ + * @param size バッファサイズ + * @param sock 対象ソケット + */ + void (*print_info)(struct KcSocket_ *sock, char *buff, size_t size); /** * Socket 管理情報 diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 7cd4ecc..402a158 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -87,6 +87,12 @@ snprintf(assert_message, sizeof(assert_message), "%s:%d: %s: Assertion expected <%s> but was <%s>", file, line, func, expected, actual); + if (strlen(actual) == 6) + { + printf("[%s]\n", actual); + printf("[%c][%02x]\n", actual[5], actual[5]); + } + assert_handler(assert_message); } } diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h index 7d952c7..f9b28ec 100644 --- a/modules/include/kc_socket.h +++ b/modules/include/kc_socket.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -42,15 +43,13 @@ { /** - * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * ソケットの接続先アドレスを返します。 + * アドレスが取得できない場合(未接続状態など)、NULL を返します。 * * @param sock 対象ソケット - * @param addr 接続先アドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットの接続先アドレス */ - const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + const char *(*get_remote_addr)(struct KcSocket_ *sock); /** * ソケットの接続先ポート番号を返します。 @@ -62,15 +61,13 @@ int (*get_remote_port)(struct KcSocket_ *sock); /** - * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * ソケットのローカルアドレスを返します。 + * アドレスが取得できない場合、NULL を返します。 * * @param sock 対象ソケット - * @param addr ローカルアドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットのローカルアドレス */ - const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + const char *(*get_local_addr)(struct KcSocket_ *sock); /** * ソケットのローカルポート番号を返します。 @@ -227,7 +224,7 @@ * @param src_addrlen 送信元アドレス格納用バッファサイズ * @param src_service 送信元サービス格納用バッファ * @param src_servicelen 送信元サービス格納用バッファサイズ - * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + * @return 受信メッセージサイズ、エラー発生時は -1 */ ssize_t (*recvfrom)( struct KcSocket_ *sock, @@ -246,23 +243,45 @@ /** * マルチキャストグループに参加するために JOIN します。 + * IPv4 の場合、ifname に、IPアドレスを指定ください。 + * IPv6 の場合、ifname にインタフェース名 (例: eth0 など)を指定ください。 + * ifname に NULL が指定された場合、任意のインタフェースとなります。 * * @param sock 対象ソケット * @param addr マルチキャストアドレス * @param ifname インタフェース名 - * @param ifindex インタフェースインデックス * @return true/false (成功/失敗) */ - bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname); /** * マルチキャストグループから離脱します。 * * @param sock 対象ソケット * @param addr マルチキャストアドレス + * @param ifname インタフェース名 * @return true/false (成功/失敗) */ - bool (*leave)(struct KcSocket_ *sock, const char *addr); + bool (*leave)(struct KcSocket_ *sock, const char *addr, const char *ifname); + + /** + * マルチキャストを送信するインタフェースを指定します。 + * + * @param sock 対象ソケット + * @param v4_ifname インタフェース名(IPアドレス) + * @param v6_ifname インタフェース名(eth0 など) + * @return true/false (成功/失敗) + */ + void (*set_ifname)(struct KcSocket_ *sock, const char *v4_ifname, const char *v6_ifname); + + /** + * 指定されたソケットの情報を指定されたバッファに出力します。 + * + * @param buff バッファ + * @param size バッファサイズ + * @param sock 対象ソケット + */ + void (*print_info)(struct KcSocket_ *sock, char *buff, size_t size); /** * Socket 管理情報 diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 7cd4ecc..402a158 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -87,6 +87,12 @@ snprintf(assert_message, sizeof(assert_message), "%s:%d: %s: Assertion expected <%s> but was <%s>", file, line, func, expected, actual); + if (strlen(actual) == 6) + { + printf("[%s]\n", actual); + printf("[%c][%02x]\n", actual[5], actual[5]); + } + assert_handler(assert_message); } } diff --git a/modules/src/kc_socket.c b/modules/src/kc_socket.c index cb67996..f957c05 100644 --- a/modules/src/kc_socket.c +++ b/modules/src/kc_socket.c @@ -3,6 +3,8 @@ * @brief ソケットモジュール * @copyright 2003 - 2024 Nomura Kei */ +#include + #include #include @@ -22,7 +24,11 @@ struct sockaddr_storage remote_addr; //!< リモートアドレス struct sockaddr_storage local_addr; //!< ローカルアドレス socklen_t remote_addrlen; //!< リモートアドレス長 - socklen_t local_addrlen; //!< ローカルアドレス帳 + socklen_t local_addrlen; //!< ローカルアドレス長 + char tmp_remote_host[NI_MAXHOST]; //!< 一時的なリモートホスト名格納用 + char tmp_local_host[NI_MAXHOST]; //!< 一時的なローカルホスト名格納用 + const char *mcast_v4_ifname; //!< マルチキャスト ifname (IPv4) + const char *mcast_v6_ifname; //!< マルチキャスト ifname (IPv6) } KcSocketInfo; /** @@ -39,41 +45,41 @@ // ============================================================================= static void Socket_setup(void); static void Socket_cleanup(void); -static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen); +static const char *KcSocket_get_remote_addr(KcSocket *sock); static int KcSocket_get_remote_port(KcSocket *sock); -static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen); +static const char *KcSocket_get_local_addr(KcSocket *sock); static int KcSocket_get_local_port(KcSocket *sock); static socket_t KcSocket_get_socket(KcSocket *sock); static int KcSocket_get_type(KcSocket *sock); static int KcSocket_get_family(KcSocket *sock); -static bool KcSocket_get_addrinfo( - KcSocket *sock, struct addrinfo **result, - const char *addr, const char *service, bool is_passive); +static bool KcSocket_get_addrinfo(KcSocket *sock, struct addrinfo **result, const char *addr, const char *service, bool is_passive); static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service); static bool KcSocket_listen(KcSocket *sock, int backlog); static KcSocket *KcSocket_accept(KcSocket *sock); -static bool KcSocket_connect( - KcSocket *sock, const char *addr, const char *service, - const char *local_addr, const char *local_service); +static bool KcSocket_connect(KcSocket *sock, const char *addr, const char *service, const char *local_addr, const char *local_service); static bool KcSocket_close(KcSocket *sock); static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags); static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags); -static ssize_t KcSocket_sendto( - KcSocket *sock, - const char *buff, size_t size, int flags, - const char *addr, const char *service); -static ssize_t KcSocket_recvfrom( - KcSocket *sock, - char *buff, size_t size, int flags, - char *src_addr, size_t src_addrlen, - char *src_service, size_t src_servicelen); +static ssize_t KcSocket_sendto(KcSocket *sock, const char *buff, size_t size, int flags, const char *addr, const char *service); +static ssize_t KcSocket_recvfrom(KcSocket *sock, char *buff, size_t size, int flags, char *src_addr, size_t src_addrlen, char *src_service, size_t src_servicelen); static bool KcSocket_set_ttl(KcSocket *sock, int val); -static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex); -static bool KcSocket_leave(KcSocket *sock, const char *addr); +static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname); +static bool KcSocket_leave(KcSocket *sock, const char *addr, const char *ifname); +static void KcSocket_set_ifname(KcSocket *sock, const char *v4_ifname, const char *v6_ifname); +static void KcSocket_print_info(KcSocket *sock, char *buff, size_t size); +// 内部関数 static bool KcSocket_addrinfo_bind_and_connect( KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo); static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd); +static int KcSocket_join_ipv4(KcSocket *sock, const char *addr, const char *ifname); +static int KcSocket_join_ipv6(KcSocket *sock, const char *addr, const char *ifname); +static int KcSocket_leave_ipv4(KcSocket *sock, const char *addr, const char *ifname); +static int KcSocket_leave_ipv6(KcSocket *sock, const char *addr, const char *ifname); +static bool KcSocket_set_mcastif(KcSocket *sock); +static int KcSocket_set_mcastif_ipv4(KcSocket *sock, const char *ifname); +static int KcSocket_set_mcastif_ipv6(KcSocket *sock, const char *ifname); +static int KcSocket_print_addr(char *buff, size_t size, const struct sockaddr *addr, size_t addrlen); /** * ソケットのセットアップをします。 @@ -145,6 +151,8 @@ sock->set_ttl = KcSocket_set_ttl; sock->join = KcSocket_join; sock->leave = KcSocket_leave; + sock->set_ifname = KcSocket_set_ifname; + sock->print_info = KcSocket_print_info; sock->_info = (sock + 1); KcSocketInfo *info = (KcSocketInfo *)sock->_info; info->sock_fd = INVALID_SOCKET; @@ -152,6 +160,10 @@ info->sock_family = family; info->remote_addrlen = 0; info->local_addrlen = 0; + info->tmp_local_host[0] = '\0'; + info->tmp_remote_host[0] = '\0'; + info->mcast_v4_ifname = NULL; + info->mcast_v6_ifname = NULL; } return sock; } @@ -178,15 +190,13 @@ // get_remote_addr // ============================================================================= /** - * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * ソケットの接続先アドレスを返します。 + * アドレスが取得できない場合(未接続状態など)、NULL を返します。 * * @param sock 対象ソケット - * @param addr 接続先アドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットの接続先アドレス */ -static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen) +static const char *KcSocket_get_remote_addr(KcSocket *sock) { KcSocketInfo *info = (KcSocketInfo *)sock->_info; if (info->remote_addrlen) @@ -194,10 +204,10 @@ int ret = getnameinfo( (const struct sockaddr *)&info->remote_addr, info->remote_addrlen, - addr, addrlen, NULL, 0, NI_NUMERICHOST); + info->tmp_remote_host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (ret == 0) { - return addr; + return info->tmp_remote_host; } } return NULL; @@ -229,26 +239,24 @@ } /** - * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * ソケットのローカルアドレスを返します。 + * アドレスが取得できない場合、NULL を返します。 * * @param sock 対象ソケット - * @param addr ローカルアドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットのローカルアドレス */ -static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen) +static const char *KcSocket_get_local_addr(KcSocket *sock) { KcSocketInfo *info = (KcSocketInfo *)sock->_info; if (info->local_addrlen) { int ret = getnameinfo( (const struct sockaddr *)&info->local_addr, - info->remote_addrlen, - addr, addrlen, NULL, 0, NI_NUMERICHOST); + info->local_addrlen, + info->tmp_local_host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (ret == 0) { - return addr; + return info->tmp_local_host; } } return NULL; @@ -269,7 +277,7 @@ char tmp_port[NI_MAXSERV]; int ret = getnameinfo( (const struct sockaddr *)&info->local_addr, - info->remote_addrlen, + info->local_addrlen, NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); if (ret == 0) { @@ -483,8 +491,7 @@ static bool KcSocket_close(KcSocket *sock) { KcSocketInfo *info = (KcSocketInfo *)sock->_info; - - if (info->sock_family == SOCK_STREAM) + if (info->sock_type == SOCK_STREAM) { // TCP の場合は、出力を閉じて、届いているデータを読み取ってから close する。 int ret = shutdown(info->sock_fd, SHUT_WR); if (ret == 0) @@ -556,17 +563,49 @@ * @return 送信メッセージサイズ、エラー発生時は -1 */ static ssize_t KcSocket_sendto( - KcSocket *sock, - const char *buff, size_t size, int flags, + KcSocket *sock, const char *buff, size_t size, int flags, const char *addr, const char *service) { - (void)sock; - (void)buff; - (void)size; - (void)flags; - (void)addr; - (void)service; - return 0; + // 接続先アドレス情報取得 + struct addrinfo *conn_addr = NULL; + if (!sock->get_addrinfo(sock, &conn_addr, addr, service, false)) + { // 接続先情報取得NG + return SOCKET_ERROR; + } + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + socket_t tmp_sock = info->sock_fd; + ssize_t send_size = SOCKET_ERROR; + for (struct addrinfo *rp = conn_addr; rp != NULL; rp = rp->ai_next) + { + if (info->sock_fd == INVALID_SOCKET) + { // sockfd が無効の場合、ソケットを生成する。 + tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + } + + // マルチキャスト用 ifname が指定されている場合は設定する。 + KcSocket_set_mcastif(sock); + + send_size = sendto(tmp_sock, buff, size, flags, rp->ai_addr, rp->ai_addrlen); + if (send_size != SOCKET_ERROR) + { // 送信成功 + info->sock_fd = tmp_sock; + info->sock_family = rp->ai_family; + info->remote_addrlen = rp->ai_addrlen; + memcpy(&info->remote_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + else + { // 送信失敗のため、一時的に生成したソケットをクローズする。 + sockclose(tmp_sock); + } + } + freeaddrinfo(conn_addr); + return send_size; } /** @@ -582,7 +621,7 @@ * @param src_addrlen 送信元アドレス格納用バッファサイズ * @param src_service 送信元サービス格納用バッファ * @param src_servicelen 送信元サービス格納用バッファサイズ - * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + * @return 受信メッセージサイズ、エラー発生時は -1 */ static ssize_t KcSocket_recvfrom( KcSocket *sock, @@ -590,15 +629,29 @@ char *src_addr, size_t src_addrlen, char *src_service, size_t src_servicelen) { - (void)sock; - (void)buff; - (void)size; - (void)flags; - (void)src_addr; - (void)src_addrlen; - (void)src_service; - (void)src_servicelen; - return 0; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t recv_size = SOCKET_ERROR; + if (info->sock_fd != INVALID_SOCKET) + { + struct sockaddr_storage src_sockaddr; + socklen_t src_sockaddrlen = sizeof(struct sockaddr_storage); + recv_size = recvfrom( + info->sock_fd, buff, size, flags, (struct sockaddr *)&src_sockaddr, &src_sockaddrlen); + + if ((src_addr != NULL) && (src_service != NULL)) + { + int ret = getnameinfo( + (struct sockaddr *)&src_sockaddr, src_addrlen, + src_addr, src_addrlen, src_service, src_servicelen, + (NI_NUMERICHOST | NI_NUMERICSERV)); + if (ret != 0) + { + src_addr[0] = '\0'; + src_service[0] = '\0'; + } + } + } + return recv_size; } /** @@ -610,28 +663,55 @@ */ static bool KcSocket_set_ttl(KcSocket *sock, int val) { - (void)sock; - (void)val; - return true; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = SOCKET_ERROR; + switch (info->sock_family) + { + case AF_INET: + unsigned char ttl; + ttl = (unsigned char)val; + ret = setsockopt(info->sock_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); + break; + case AF_INET6: + int hop; + hop = val; + ret = setsockopt(info->sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hop, sizeof(hop)); + break; + default: + // unspoorted socket family + break; + } + return (ret == 0); } /** * マルチキャストグループに参加するために JOIN します。 + * IPv4 の場合、ifname に、IPアドレスを指定ください。 + * IPv6 の場合、ifname にインタフェース名 (例: eth0 など)を指定ください。 + * ifname に NULL が指定された場合、任意のインタフェースとなります。 * * @param sock 対象ソケット * @param addr マルチキャストアドレス * @param ifname インタフェース名 - * @param ifindex インタフェースインデックス * @return true/false (成功/失敗) */ -static bool KcSocket_join( - KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex) +static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname) { - (void)sock; - (void)addr; - (void)ifname; - (void)ifindex; - return true; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = SOCKET_ERROR; + switch (info->sock_family) + { + case AF_INET: + ret = KcSocket_join_ipv4(sock, addr, ifname); + break; + case AF_INET6: + ret = KcSocket_join_ipv6(sock, addr, ifname); + break; + default: + // NOP + break; + } + return (ret == 0); } /** @@ -639,14 +719,63 @@ * * @param sock 対象ソケット * @param addr マルチキャストアドレス + * @param ifname インタフェース名 * @return true/false (成功/失敗) */ -static bool KcSocket_leave( - KcSocket *sock, const char *addr) +static bool KcSocket_leave(KcSocket *sock, const char *addr, const char *ifname) { - (void)sock; - (void)addr; - return true; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = SOCKET_ERROR; + switch (info->sock_family) + { + case AF_INET: + ret = KcSocket_leave_ipv4(sock, addr, ifname); + break; + case AF_INET6: + ret = KcSocket_leave_ipv6(sock, addr, ifname); + break; + default: + // NOP + break; + } + return (ret == 0); +} + +/** + * マルチキャストを送信するインタフェースを指定します。 + * + * @param sock 対象ソケット + * @param v4_ifname インタフェース名(IPアドレス) + * @param v6_ifname インタフェース名(eth0 など) + * @return true/false (成功/失敗) + */ +static void KcSocket_set_ifname(KcSocket *sock, const char *v4_ifname, const char *v6_ifname) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->mcast_v4_ifname = v4_ifname; + info->mcast_v6_ifname = v6_ifname; +} + +/** + * 指定されたソケットの情報を指定されたバッファに出力します。 + * + * @param sock 対象ソケット + * @param buff バッファ + * @param size バッファのサイズ + */ +static void KcSocket_print_info(KcSocket *sock, char *buff, size_t size) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + char *write_ptr = buff; + char *end_ptr = &buff[size]; + write_ptr += snprintf(write_ptr, (end_ptr - write_ptr), "local : "); + write_ptr += KcSocket_print_addr(write_ptr, (end_ptr - write_ptr), (struct sockaddr *)&info->local_addr, info->local_addrlen); + write_ptr += snprintf(write_ptr, (end_ptr - write_ptr), "\n"); + write_ptr += snprintf(write_ptr, (end_ptr - write_ptr), "remote: "); + write_ptr += KcSocket_print_addr(write_ptr, (end_ptr - write_ptr), (struct sockaddr *)&info->remote_addr, info->remote_addrlen); + write_ptr += snprintf(write_ptr, (end_ptr - write_ptr), "\n"); + UNUSED_VARIABLE(write_ptr); } //////////////////////////////////////////////////////////////////////////////// @@ -684,12 +813,13 @@ { KcSocketInfo *info = (KcSocketInfo *)sock->_info; info->sock_fd = tmp_sock; + info->sock_family = rp->ai_family; info->remote_addrlen = rp->ai_addrlen; memcpy(&info->remote_addr, rp->ai_addr, rp->ai_addrlen); break; } - if (sockfd != INVALID_SOCKET) + if (sockfd == INVALID_SOCKET) { // sockfd が無効の場合、一時的に生成したソケットをクローズする。 sockclose(tmp_sock); } @@ -724,7 +854,7 @@ const int on = 1; setsockopt(tmp_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); ret = bind(tmp_sock, rp->ai_addr, rp->ai_addrlen); - is_success = (ret != SOCKET_ERROR); + is_success = (ret == 0); if (!is_success) { // bind 失敗したので次へ sockclose(tmp_sock); @@ -745,9 +875,180 @@ // bind または、bind と connect 成功 KcSocketInfo *info = (KcSocketInfo *)sock->_info; info->sock_fd = tmp_sock; + info->sock_family = rp->ai_family; info->local_addrlen = rp->ai_addrlen; memcpy(&info->local_addr, rp->ai_addr, rp->ai_addrlen); break; } return is_success; } + +/** + * マルチキャストグループに参加するために JOIN (IPv4)します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名(IP) + * @return 0/-1(成功/失敗) + */ +static int KcSocket_join_ipv4(KcSocket *sock, const char *addr, const char *ifname) +{ + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = inet_addr(addr); + mreq.imr_interface.s_addr = (ifname != NULL) ? inet_addr(ifname) : htonl(INADDR_ANY); + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = setsockopt(info->sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + return ret; +} + +/** + * マルチキャストグループに参加するために JOIN (IPv6)します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_join_ipv6(KcSocket *sock, const char *addr, const char *ifname) +{ + struct ipv6_mreq mreq6; + mreq6.ipv6mr_interface = (ifname != NULL) ? if_nametoindex(ifname) : 0; + int ret = inet_pton(AF_INET6, addr, &mreq6.ipv6mr_multiaddr); + if (ret != SOCKET_ERROR) + { + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ret = setsockopt(info->sock_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6)); + } + return ret; +} + +/** + * マルチキャストグループから離脱します(IPv4)。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名(IP) + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_leave_ipv4(KcSocket *sock, const char *addr, const char *ifname) +{ + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = inet_addr(addr); + mreq.imr_interface.s_addr = (ifname != NULL) ? inet_addr(ifname) : htonl(INADDR_ANY); + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = setsockopt(info->sock_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); + return ret; +} + +/** + * マルチキャストグループに離脱します(IPv6)。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_leave_ipv6(KcSocket *sock, const char *addr, const char *ifname) +{ + struct ipv6_mreq mreq6; + mreq6.ipv6mr_interface = (ifname != NULL) ? if_nametoindex(ifname) : 0; + int ret = inet_pton(AF_INET6, addr, &mreq6.ipv6mr_multiaddr); + if (ret != SOCKET_ERROR) + { + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ret = setsockopt(info->sock_fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq6, sizeof(mreq6)); + } + return ret; +} + +/** + * マルチキャストを送信するためのインタフェースを設定します。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ +static bool KcSocket_set_mcastif(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = SOCKET_ERROR; + switch (info->sock_family) + { + case AF_INET: + ret = (info->mcast_v4_ifname) + ? KcSocket_set_mcastif_ipv4(sock, info->mcast_v4_ifname) + : 0; + break; + case AF_INET6: + ret = (info->mcast_v6_ifname) + ? KcSocket_set_mcastif_ipv6(sock, info->mcast_v6_ifname) + : 0; + break; + default: + // NOP + break; + } + return (ret == 0); +} + +/** + * マルチキャストを送信するためのインタフェースを設定します(IPv4)。 + * + * @param sock 対象ソケット + * @param ifname インタフェース名 + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_set_mcastif_ipv4(KcSocket *sock, const char *ifname) +{ + struct in_addr ifaddr; + ifaddr.s_addr = (ifname != NULL) ? inet_addr(ifname) : htonl(INADDR_ANY); + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = setsockopt(info->sock_fd, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr, sizeof(ifaddr)); + return ret; +} + +/** + * マルチキャストを送信するためのインタフェースを設定します(IPv6)。 + * + * @param sock 対象ソケット + * @param ifname インタフェース名 + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_set_mcastif_ipv6(KcSocket *sock, const char *ifname) +{ + unsigned int ifindex = (ifname != NULL) ? if_nametoindex(ifname) : 0; + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = setsockopt(info->sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)); + return ret; +} + +/** + * 指定された addr の情報を出力します。 + * + * @param buff バッファ + * @param size サイズ + * @param addr アドレス + * @param addrlen アドレス長 + * @return バッファに書き出したバイト数 + */ +static int KcSocket_print_addr(char *buff, size_t size, const struct sockaddr *addr, size_t addrlen) +{ + char tmp_addr[NI_MAXHOST]; + char tmp_service[NI_MAXSERV]; + int ret = getnameinfo( + addr, addrlen, + tmp_addr, sizeof(tmp_addr), tmp_service, sizeof(tmp_service), NI_NUMERICHOST | NI_NUMERICSERV); + int write_size = 0; + if (ret == 0) + { + write_size = snprintf(buff, size, "%s:%s", tmp_addr, tmp_service); + } + else + { + write_size = snprintf(buff, size, "-:-"); + } + return write_size; +} diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h index 7d952c7..f9b28ec 100644 --- a/modules/include/kc_socket.h +++ b/modules/include/kc_socket.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -42,15 +43,13 @@ { /** - * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * ソケットの接続先アドレスを返します。 + * アドレスが取得できない場合(未接続状態など)、NULL を返します。 * * @param sock 対象ソケット - * @param addr 接続先アドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットの接続先アドレス */ - const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + const char *(*get_remote_addr)(struct KcSocket_ *sock); /** * ソケットの接続先ポート番号を返します。 @@ -62,15 +61,13 @@ int (*get_remote_port)(struct KcSocket_ *sock); /** - * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * ソケットのローカルアドレスを返します。 + * アドレスが取得できない場合、NULL を返します。 * * @param sock 対象ソケット - * @param addr ローカルアドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットのローカルアドレス */ - const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + const char *(*get_local_addr)(struct KcSocket_ *sock); /** * ソケットのローカルポート番号を返します。 @@ -227,7 +224,7 @@ * @param src_addrlen 送信元アドレス格納用バッファサイズ * @param src_service 送信元サービス格納用バッファ * @param src_servicelen 送信元サービス格納用バッファサイズ - * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + * @return 受信メッセージサイズ、エラー発生時は -1 */ ssize_t (*recvfrom)( struct KcSocket_ *sock, @@ -246,23 +243,45 @@ /** * マルチキャストグループに参加するために JOIN します。 + * IPv4 の場合、ifname に、IPアドレスを指定ください。 + * IPv6 の場合、ifname にインタフェース名 (例: eth0 など)を指定ください。 + * ifname に NULL が指定された場合、任意のインタフェースとなります。 * * @param sock 対象ソケット * @param addr マルチキャストアドレス * @param ifname インタフェース名 - * @param ifindex インタフェースインデックス * @return true/false (成功/失敗) */ - bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname); /** * マルチキャストグループから離脱します。 * * @param sock 対象ソケット * @param addr マルチキャストアドレス + * @param ifname インタフェース名 * @return true/false (成功/失敗) */ - bool (*leave)(struct KcSocket_ *sock, const char *addr); + bool (*leave)(struct KcSocket_ *sock, const char *addr, const char *ifname); + + /** + * マルチキャストを送信するインタフェースを指定します。 + * + * @param sock 対象ソケット + * @param v4_ifname インタフェース名(IPアドレス) + * @param v6_ifname インタフェース名(eth0 など) + * @return true/false (成功/失敗) + */ + void (*set_ifname)(struct KcSocket_ *sock, const char *v4_ifname, const char *v6_ifname); + + /** + * 指定されたソケットの情報を指定されたバッファに出力します。 + * + * @param buff バッファ + * @param size バッファサイズ + * @param sock 対象ソケット + */ + void (*print_info)(struct KcSocket_ *sock, char *buff, size_t size); /** * Socket 管理情報 diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 7cd4ecc..402a158 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -87,6 +87,12 @@ snprintf(assert_message, sizeof(assert_message), "%s:%d: %s: Assertion expected <%s> but was <%s>", file, line, func, expected, actual); + if (strlen(actual) == 6) + { + printf("[%s]\n", actual); + printf("[%c][%02x]\n", actual[5], actual[5]); + } + assert_handler(assert_message); } } diff --git a/modules/src/kc_socket.c b/modules/src/kc_socket.c index cb67996..f957c05 100644 --- a/modules/src/kc_socket.c +++ b/modules/src/kc_socket.c @@ -3,6 +3,8 @@ * @brief ソケットモジュール * @copyright 2003 - 2024 Nomura Kei */ +#include + #include #include @@ -22,7 +24,11 @@ struct sockaddr_storage remote_addr; //!< リモートアドレス struct sockaddr_storage local_addr; //!< ローカルアドレス socklen_t remote_addrlen; //!< リモートアドレス長 - socklen_t local_addrlen; //!< ローカルアドレス帳 + socklen_t local_addrlen; //!< ローカルアドレス長 + char tmp_remote_host[NI_MAXHOST]; //!< 一時的なリモートホスト名格納用 + char tmp_local_host[NI_MAXHOST]; //!< 一時的なローカルホスト名格納用 + const char *mcast_v4_ifname; //!< マルチキャスト ifname (IPv4) + const char *mcast_v6_ifname; //!< マルチキャスト ifname (IPv6) } KcSocketInfo; /** @@ -39,41 +45,41 @@ // ============================================================================= static void Socket_setup(void); static void Socket_cleanup(void); -static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen); +static const char *KcSocket_get_remote_addr(KcSocket *sock); static int KcSocket_get_remote_port(KcSocket *sock); -static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen); +static const char *KcSocket_get_local_addr(KcSocket *sock); static int KcSocket_get_local_port(KcSocket *sock); static socket_t KcSocket_get_socket(KcSocket *sock); static int KcSocket_get_type(KcSocket *sock); static int KcSocket_get_family(KcSocket *sock); -static bool KcSocket_get_addrinfo( - KcSocket *sock, struct addrinfo **result, - const char *addr, const char *service, bool is_passive); +static bool KcSocket_get_addrinfo(KcSocket *sock, struct addrinfo **result, const char *addr, const char *service, bool is_passive); static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service); static bool KcSocket_listen(KcSocket *sock, int backlog); static KcSocket *KcSocket_accept(KcSocket *sock); -static bool KcSocket_connect( - KcSocket *sock, const char *addr, const char *service, - const char *local_addr, const char *local_service); +static bool KcSocket_connect(KcSocket *sock, const char *addr, const char *service, const char *local_addr, const char *local_service); static bool KcSocket_close(KcSocket *sock); static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags); static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags); -static ssize_t KcSocket_sendto( - KcSocket *sock, - const char *buff, size_t size, int flags, - const char *addr, const char *service); -static ssize_t KcSocket_recvfrom( - KcSocket *sock, - char *buff, size_t size, int flags, - char *src_addr, size_t src_addrlen, - char *src_service, size_t src_servicelen); +static ssize_t KcSocket_sendto(KcSocket *sock, const char *buff, size_t size, int flags, const char *addr, const char *service); +static ssize_t KcSocket_recvfrom(KcSocket *sock, char *buff, size_t size, int flags, char *src_addr, size_t src_addrlen, char *src_service, size_t src_servicelen); static bool KcSocket_set_ttl(KcSocket *sock, int val); -static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex); -static bool KcSocket_leave(KcSocket *sock, const char *addr); +static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname); +static bool KcSocket_leave(KcSocket *sock, const char *addr, const char *ifname); +static void KcSocket_set_ifname(KcSocket *sock, const char *v4_ifname, const char *v6_ifname); +static void KcSocket_print_info(KcSocket *sock, char *buff, size_t size); +// 内部関数 static bool KcSocket_addrinfo_bind_and_connect( KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo); static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd); +static int KcSocket_join_ipv4(KcSocket *sock, const char *addr, const char *ifname); +static int KcSocket_join_ipv6(KcSocket *sock, const char *addr, const char *ifname); +static int KcSocket_leave_ipv4(KcSocket *sock, const char *addr, const char *ifname); +static int KcSocket_leave_ipv6(KcSocket *sock, const char *addr, const char *ifname); +static bool KcSocket_set_mcastif(KcSocket *sock); +static int KcSocket_set_mcastif_ipv4(KcSocket *sock, const char *ifname); +static int KcSocket_set_mcastif_ipv6(KcSocket *sock, const char *ifname); +static int KcSocket_print_addr(char *buff, size_t size, const struct sockaddr *addr, size_t addrlen); /** * ソケットのセットアップをします。 @@ -145,6 +151,8 @@ sock->set_ttl = KcSocket_set_ttl; sock->join = KcSocket_join; sock->leave = KcSocket_leave; + sock->set_ifname = KcSocket_set_ifname; + sock->print_info = KcSocket_print_info; sock->_info = (sock + 1); KcSocketInfo *info = (KcSocketInfo *)sock->_info; info->sock_fd = INVALID_SOCKET; @@ -152,6 +160,10 @@ info->sock_family = family; info->remote_addrlen = 0; info->local_addrlen = 0; + info->tmp_local_host[0] = '\0'; + info->tmp_remote_host[0] = '\0'; + info->mcast_v4_ifname = NULL; + info->mcast_v6_ifname = NULL; } return sock; } @@ -178,15 +190,13 @@ // get_remote_addr // ============================================================================= /** - * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * ソケットの接続先アドレスを返します。 + * アドレスが取得できない場合(未接続状態など)、NULL を返します。 * * @param sock 対象ソケット - * @param addr 接続先アドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットの接続先アドレス */ -static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen) +static const char *KcSocket_get_remote_addr(KcSocket *sock) { KcSocketInfo *info = (KcSocketInfo *)sock->_info; if (info->remote_addrlen) @@ -194,10 +204,10 @@ int ret = getnameinfo( (const struct sockaddr *)&info->remote_addr, info->remote_addrlen, - addr, addrlen, NULL, 0, NI_NUMERICHOST); + info->tmp_remote_host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (ret == 0) { - return addr; + return info->tmp_remote_host; } } return NULL; @@ -229,26 +239,24 @@ } /** - * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * ソケットのローカルアドレスを返します。 + * アドレスが取得できない場合、NULL を返します。 * * @param sock 対象ソケット - * @param addr ローカルアドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットのローカルアドレス */ -static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen) +static const char *KcSocket_get_local_addr(KcSocket *sock) { KcSocketInfo *info = (KcSocketInfo *)sock->_info; if (info->local_addrlen) { int ret = getnameinfo( (const struct sockaddr *)&info->local_addr, - info->remote_addrlen, - addr, addrlen, NULL, 0, NI_NUMERICHOST); + info->local_addrlen, + info->tmp_local_host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (ret == 0) { - return addr; + return info->tmp_local_host; } } return NULL; @@ -269,7 +277,7 @@ char tmp_port[NI_MAXSERV]; int ret = getnameinfo( (const struct sockaddr *)&info->local_addr, - info->remote_addrlen, + info->local_addrlen, NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); if (ret == 0) { @@ -483,8 +491,7 @@ static bool KcSocket_close(KcSocket *sock) { KcSocketInfo *info = (KcSocketInfo *)sock->_info; - - if (info->sock_family == SOCK_STREAM) + if (info->sock_type == SOCK_STREAM) { // TCP の場合は、出力を閉じて、届いているデータを読み取ってから close する。 int ret = shutdown(info->sock_fd, SHUT_WR); if (ret == 0) @@ -556,17 +563,49 @@ * @return 送信メッセージサイズ、エラー発生時は -1 */ static ssize_t KcSocket_sendto( - KcSocket *sock, - const char *buff, size_t size, int flags, + KcSocket *sock, const char *buff, size_t size, int flags, const char *addr, const char *service) { - (void)sock; - (void)buff; - (void)size; - (void)flags; - (void)addr; - (void)service; - return 0; + // 接続先アドレス情報取得 + struct addrinfo *conn_addr = NULL; + if (!sock->get_addrinfo(sock, &conn_addr, addr, service, false)) + { // 接続先情報取得NG + return SOCKET_ERROR; + } + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + socket_t tmp_sock = info->sock_fd; + ssize_t send_size = SOCKET_ERROR; + for (struct addrinfo *rp = conn_addr; rp != NULL; rp = rp->ai_next) + { + if (info->sock_fd == INVALID_SOCKET) + { // sockfd が無効の場合、ソケットを生成する。 + tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + } + + // マルチキャスト用 ifname が指定されている場合は設定する。 + KcSocket_set_mcastif(sock); + + send_size = sendto(tmp_sock, buff, size, flags, rp->ai_addr, rp->ai_addrlen); + if (send_size != SOCKET_ERROR) + { // 送信成功 + info->sock_fd = tmp_sock; + info->sock_family = rp->ai_family; + info->remote_addrlen = rp->ai_addrlen; + memcpy(&info->remote_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + else + { // 送信失敗のため、一時的に生成したソケットをクローズする。 + sockclose(tmp_sock); + } + } + freeaddrinfo(conn_addr); + return send_size; } /** @@ -582,7 +621,7 @@ * @param src_addrlen 送信元アドレス格納用バッファサイズ * @param src_service 送信元サービス格納用バッファ * @param src_servicelen 送信元サービス格納用バッファサイズ - * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + * @return 受信メッセージサイズ、エラー発生時は -1 */ static ssize_t KcSocket_recvfrom( KcSocket *sock, @@ -590,15 +629,29 @@ char *src_addr, size_t src_addrlen, char *src_service, size_t src_servicelen) { - (void)sock; - (void)buff; - (void)size; - (void)flags; - (void)src_addr; - (void)src_addrlen; - (void)src_service; - (void)src_servicelen; - return 0; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t recv_size = SOCKET_ERROR; + if (info->sock_fd != INVALID_SOCKET) + { + struct sockaddr_storage src_sockaddr; + socklen_t src_sockaddrlen = sizeof(struct sockaddr_storage); + recv_size = recvfrom( + info->sock_fd, buff, size, flags, (struct sockaddr *)&src_sockaddr, &src_sockaddrlen); + + if ((src_addr != NULL) && (src_service != NULL)) + { + int ret = getnameinfo( + (struct sockaddr *)&src_sockaddr, src_addrlen, + src_addr, src_addrlen, src_service, src_servicelen, + (NI_NUMERICHOST | NI_NUMERICSERV)); + if (ret != 0) + { + src_addr[0] = '\0'; + src_service[0] = '\0'; + } + } + } + return recv_size; } /** @@ -610,28 +663,55 @@ */ static bool KcSocket_set_ttl(KcSocket *sock, int val) { - (void)sock; - (void)val; - return true; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = SOCKET_ERROR; + switch (info->sock_family) + { + case AF_INET: + unsigned char ttl; + ttl = (unsigned char)val; + ret = setsockopt(info->sock_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); + break; + case AF_INET6: + int hop; + hop = val; + ret = setsockopt(info->sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hop, sizeof(hop)); + break; + default: + // unspoorted socket family + break; + } + return (ret == 0); } /** * マルチキャストグループに参加するために JOIN します。 + * IPv4 の場合、ifname に、IPアドレスを指定ください。 + * IPv6 の場合、ifname にインタフェース名 (例: eth0 など)を指定ください。 + * ifname に NULL が指定された場合、任意のインタフェースとなります。 * * @param sock 対象ソケット * @param addr マルチキャストアドレス * @param ifname インタフェース名 - * @param ifindex インタフェースインデックス * @return true/false (成功/失敗) */ -static bool KcSocket_join( - KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex) +static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname) { - (void)sock; - (void)addr; - (void)ifname; - (void)ifindex; - return true; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = SOCKET_ERROR; + switch (info->sock_family) + { + case AF_INET: + ret = KcSocket_join_ipv4(sock, addr, ifname); + break; + case AF_INET6: + ret = KcSocket_join_ipv6(sock, addr, ifname); + break; + default: + // NOP + break; + } + return (ret == 0); } /** @@ -639,14 +719,63 @@ * * @param sock 対象ソケット * @param addr マルチキャストアドレス + * @param ifname インタフェース名 * @return true/false (成功/失敗) */ -static bool KcSocket_leave( - KcSocket *sock, const char *addr) +static bool KcSocket_leave(KcSocket *sock, const char *addr, const char *ifname) { - (void)sock; - (void)addr; - return true; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = SOCKET_ERROR; + switch (info->sock_family) + { + case AF_INET: + ret = KcSocket_leave_ipv4(sock, addr, ifname); + break; + case AF_INET6: + ret = KcSocket_leave_ipv6(sock, addr, ifname); + break; + default: + // NOP + break; + } + return (ret == 0); +} + +/** + * マルチキャストを送信するインタフェースを指定します。 + * + * @param sock 対象ソケット + * @param v4_ifname インタフェース名(IPアドレス) + * @param v6_ifname インタフェース名(eth0 など) + * @return true/false (成功/失敗) + */ +static void KcSocket_set_ifname(KcSocket *sock, const char *v4_ifname, const char *v6_ifname) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->mcast_v4_ifname = v4_ifname; + info->mcast_v6_ifname = v6_ifname; +} + +/** + * 指定されたソケットの情報を指定されたバッファに出力します。 + * + * @param sock 対象ソケット + * @param buff バッファ + * @param size バッファのサイズ + */ +static void KcSocket_print_info(KcSocket *sock, char *buff, size_t size) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + char *write_ptr = buff; + char *end_ptr = &buff[size]; + write_ptr += snprintf(write_ptr, (end_ptr - write_ptr), "local : "); + write_ptr += KcSocket_print_addr(write_ptr, (end_ptr - write_ptr), (struct sockaddr *)&info->local_addr, info->local_addrlen); + write_ptr += snprintf(write_ptr, (end_ptr - write_ptr), "\n"); + write_ptr += snprintf(write_ptr, (end_ptr - write_ptr), "remote: "); + write_ptr += KcSocket_print_addr(write_ptr, (end_ptr - write_ptr), (struct sockaddr *)&info->remote_addr, info->remote_addrlen); + write_ptr += snprintf(write_ptr, (end_ptr - write_ptr), "\n"); + UNUSED_VARIABLE(write_ptr); } //////////////////////////////////////////////////////////////////////////////// @@ -684,12 +813,13 @@ { KcSocketInfo *info = (KcSocketInfo *)sock->_info; info->sock_fd = tmp_sock; + info->sock_family = rp->ai_family; info->remote_addrlen = rp->ai_addrlen; memcpy(&info->remote_addr, rp->ai_addr, rp->ai_addrlen); break; } - if (sockfd != INVALID_SOCKET) + if (sockfd == INVALID_SOCKET) { // sockfd が無効の場合、一時的に生成したソケットをクローズする。 sockclose(tmp_sock); } @@ -724,7 +854,7 @@ const int on = 1; setsockopt(tmp_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); ret = bind(tmp_sock, rp->ai_addr, rp->ai_addrlen); - is_success = (ret != SOCKET_ERROR); + is_success = (ret == 0); if (!is_success) { // bind 失敗したので次へ sockclose(tmp_sock); @@ -745,9 +875,180 @@ // bind または、bind と connect 成功 KcSocketInfo *info = (KcSocketInfo *)sock->_info; info->sock_fd = tmp_sock; + info->sock_family = rp->ai_family; info->local_addrlen = rp->ai_addrlen; memcpy(&info->local_addr, rp->ai_addr, rp->ai_addrlen); break; } return is_success; } + +/** + * マルチキャストグループに参加するために JOIN (IPv4)します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名(IP) + * @return 0/-1(成功/失敗) + */ +static int KcSocket_join_ipv4(KcSocket *sock, const char *addr, const char *ifname) +{ + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = inet_addr(addr); + mreq.imr_interface.s_addr = (ifname != NULL) ? inet_addr(ifname) : htonl(INADDR_ANY); + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = setsockopt(info->sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + return ret; +} + +/** + * マルチキャストグループに参加するために JOIN (IPv6)します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_join_ipv6(KcSocket *sock, const char *addr, const char *ifname) +{ + struct ipv6_mreq mreq6; + mreq6.ipv6mr_interface = (ifname != NULL) ? if_nametoindex(ifname) : 0; + int ret = inet_pton(AF_INET6, addr, &mreq6.ipv6mr_multiaddr); + if (ret != SOCKET_ERROR) + { + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ret = setsockopt(info->sock_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6)); + } + return ret; +} + +/** + * マルチキャストグループから離脱します(IPv4)。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名(IP) + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_leave_ipv4(KcSocket *sock, const char *addr, const char *ifname) +{ + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = inet_addr(addr); + mreq.imr_interface.s_addr = (ifname != NULL) ? inet_addr(ifname) : htonl(INADDR_ANY); + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = setsockopt(info->sock_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); + return ret; +} + +/** + * マルチキャストグループに離脱します(IPv6)。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_leave_ipv6(KcSocket *sock, const char *addr, const char *ifname) +{ + struct ipv6_mreq mreq6; + mreq6.ipv6mr_interface = (ifname != NULL) ? if_nametoindex(ifname) : 0; + int ret = inet_pton(AF_INET6, addr, &mreq6.ipv6mr_multiaddr); + if (ret != SOCKET_ERROR) + { + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ret = setsockopt(info->sock_fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq6, sizeof(mreq6)); + } + return ret; +} + +/** + * マルチキャストを送信するためのインタフェースを設定します。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ +static bool KcSocket_set_mcastif(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = SOCKET_ERROR; + switch (info->sock_family) + { + case AF_INET: + ret = (info->mcast_v4_ifname) + ? KcSocket_set_mcastif_ipv4(sock, info->mcast_v4_ifname) + : 0; + break; + case AF_INET6: + ret = (info->mcast_v6_ifname) + ? KcSocket_set_mcastif_ipv6(sock, info->mcast_v6_ifname) + : 0; + break; + default: + // NOP + break; + } + return (ret == 0); +} + +/** + * マルチキャストを送信するためのインタフェースを設定します(IPv4)。 + * + * @param sock 対象ソケット + * @param ifname インタフェース名 + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_set_mcastif_ipv4(KcSocket *sock, const char *ifname) +{ + struct in_addr ifaddr; + ifaddr.s_addr = (ifname != NULL) ? inet_addr(ifname) : htonl(INADDR_ANY); + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = setsockopt(info->sock_fd, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr, sizeof(ifaddr)); + return ret; +} + +/** + * マルチキャストを送信するためのインタフェースを設定します(IPv6)。 + * + * @param sock 対象ソケット + * @param ifname インタフェース名 + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_set_mcastif_ipv6(KcSocket *sock, const char *ifname) +{ + unsigned int ifindex = (ifname != NULL) ? if_nametoindex(ifname) : 0; + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = setsockopt(info->sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)); + return ret; +} + +/** + * 指定された addr の情報を出力します。 + * + * @param buff バッファ + * @param size サイズ + * @param addr アドレス + * @param addrlen アドレス長 + * @return バッファに書き出したバイト数 + */ +static int KcSocket_print_addr(char *buff, size_t size, const struct sockaddr *addr, size_t addrlen) +{ + char tmp_addr[NI_MAXHOST]; + char tmp_service[NI_MAXSERV]; + int ret = getnameinfo( + addr, addrlen, + tmp_addr, sizeof(tmp_addr), tmp_service, sizeof(tmp_service), NI_NUMERICHOST | NI_NUMERICSERV); + int write_size = 0; + if (ret == 0) + { + write_size = snprintf(buff, size, "%s:%s", tmp_addr, tmp_service); + } + else + { + write_size = snprintf(buff, size, "-:-"); + } + return write_size; +} diff --git a/modules/test/src/test_memory_entry.c b/modules/test/src/test_memory_entry.c index e883e07..b21d089 100644 --- a/modules/test/src/test_memory_entry.c +++ b/modules/test/src/test_memory_entry.c @@ -96,6 +96,6 @@ */ static void test_memory_entry_set_null(void) { - KcMemoryEntry_set(NULL, - 10, KC_MEMORY_ALLOCATED, "test_file", "test_func", 123); + KcMemoryEntry_set( + NULL, 10, KC_MEMORY_ALLOCATED, "test_file", "test_func", 123); } diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h index 7d952c7..f9b28ec 100644 --- a/modules/include/kc_socket.h +++ b/modules/include/kc_socket.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -42,15 +43,13 @@ { /** - * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * ソケットの接続先アドレスを返します。 + * アドレスが取得できない場合(未接続状態など)、NULL を返します。 * * @param sock 対象ソケット - * @param addr 接続先アドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットの接続先アドレス */ - const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + const char *(*get_remote_addr)(struct KcSocket_ *sock); /** * ソケットの接続先ポート番号を返します。 @@ -62,15 +61,13 @@ int (*get_remote_port)(struct KcSocket_ *sock); /** - * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * ソケットのローカルアドレスを返します。 + * アドレスが取得できない場合、NULL を返します。 * * @param sock 対象ソケット - * @param addr ローカルアドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットのローカルアドレス */ - const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + const char *(*get_local_addr)(struct KcSocket_ *sock); /** * ソケットのローカルポート番号を返します。 @@ -227,7 +224,7 @@ * @param src_addrlen 送信元アドレス格納用バッファサイズ * @param src_service 送信元サービス格納用バッファ * @param src_servicelen 送信元サービス格納用バッファサイズ - * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + * @return 受信メッセージサイズ、エラー発生時は -1 */ ssize_t (*recvfrom)( struct KcSocket_ *sock, @@ -246,23 +243,45 @@ /** * マルチキャストグループに参加するために JOIN します。 + * IPv4 の場合、ifname に、IPアドレスを指定ください。 + * IPv6 の場合、ifname にインタフェース名 (例: eth0 など)を指定ください。 + * ifname に NULL が指定された場合、任意のインタフェースとなります。 * * @param sock 対象ソケット * @param addr マルチキャストアドレス * @param ifname インタフェース名 - * @param ifindex インタフェースインデックス * @return true/false (成功/失敗) */ - bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname); /** * マルチキャストグループから離脱します。 * * @param sock 対象ソケット * @param addr マルチキャストアドレス + * @param ifname インタフェース名 * @return true/false (成功/失敗) */ - bool (*leave)(struct KcSocket_ *sock, const char *addr); + bool (*leave)(struct KcSocket_ *sock, const char *addr, const char *ifname); + + /** + * マルチキャストを送信するインタフェースを指定します。 + * + * @param sock 対象ソケット + * @param v4_ifname インタフェース名(IPアドレス) + * @param v6_ifname インタフェース名(eth0 など) + * @return true/false (成功/失敗) + */ + void (*set_ifname)(struct KcSocket_ *sock, const char *v4_ifname, const char *v6_ifname); + + /** + * 指定されたソケットの情報を指定されたバッファに出力します。 + * + * @param buff バッファ + * @param size バッファサイズ + * @param sock 対象ソケット + */ + void (*print_info)(struct KcSocket_ *sock, char *buff, size_t size); /** * Socket 管理情報 diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 7cd4ecc..402a158 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -87,6 +87,12 @@ snprintf(assert_message, sizeof(assert_message), "%s:%d: %s: Assertion expected <%s> but was <%s>", file, line, func, expected, actual); + if (strlen(actual) == 6) + { + printf("[%s]\n", actual); + printf("[%c][%02x]\n", actual[5], actual[5]); + } + assert_handler(assert_message); } } diff --git a/modules/src/kc_socket.c b/modules/src/kc_socket.c index cb67996..f957c05 100644 --- a/modules/src/kc_socket.c +++ b/modules/src/kc_socket.c @@ -3,6 +3,8 @@ * @brief ソケットモジュール * @copyright 2003 - 2024 Nomura Kei */ +#include + #include #include @@ -22,7 +24,11 @@ struct sockaddr_storage remote_addr; //!< リモートアドレス struct sockaddr_storage local_addr; //!< ローカルアドレス socklen_t remote_addrlen; //!< リモートアドレス長 - socklen_t local_addrlen; //!< ローカルアドレス帳 + socklen_t local_addrlen; //!< ローカルアドレス長 + char tmp_remote_host[NI_MAXHOST]; //!< 一時的なリモートホスト名格納用 + char tmp_local_host[NI_MAXHOST]; //!< 一時的なローカルホスト名格納用 + const char *mcast_v4_ifname; //!< マルチキャスト ifname (IPv4) + const char *mcast_v6_ifname; //!< マルチキャスト ifname (IPv6) } KcSocketInfo; /** @@ -39,41 +45,41 @@ // ============================================================================= static void Socket_setup(void); static void Socket_cleanup(void); -static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen); +static const char *KcSocket_get_remote_addr(KcSocket *sock); static int KcSocket_get_remote_port(KcSocket *sock); -static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen); +static const char *KcSocket_get_local_addr(KcSocket *sock); static int KcSocket_get_local_port(KcSocket *sock); static socket_t KcSocket_get_socket(KcSocket *sock); static int KcSocket_get_type(KcSocket *sock); static int KcSocket_get_family(KcSocket *sock); -static bool KcSocket_get_addrinfo( - KcSocket *sock, struct addrinfo **result, - const char *addr, const char *service, bool is_passive); +static bool KcSocket_get_addrinfo(KcSocket *sock, struct addrinfo **result, const char *addr, const char *service, bool is_passive); static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service); static bool KcSocket_listen(KcSocket *sock, int backlog); static KcSocket *KcSocket_accept(KcSocket *sock); -static bool KcSocket_connect( - KcSocket *sock, const char *addr, const char *service, - const char *local_addr, const char *local_service); +static bool KcSocket_connect(KcSocket *sock, const char *addr, const char *service, const char *local_addr, const char *local_service); static bool KcSocket_close(KcSocket *sock); static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags); static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags); -static ssize_t KcSocket_sendto( - KcSocket *sock, - const char *buff, size_t size, int flags, - const char *addr, const char *service); -static ssize_t KcSocket_recvfrom( - KcSocket *sock, - char *buff, size_t size, int flags, - char *src_addr, size_t src_addrlen, - char *src_service, size_t src_servicelen); +static ssize_t KcSocket_sendto(KcSocket *sock, const char *buff, size_t size, int flags, const char *addr, const char *service); +static ssize_t KcSocket_recvfrom(KcSocket *sock, char *buff, size_t size, int flags, char *src_addr, size_t src_addrlen, char *src_service, size_t src_servicelen); static bool KcSocket_set_ttl(KcSocket *sock, int val); -static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex); -static bool KcSocket_leave(KcSocket *sock, const char *addr); +static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname); +static bool KcSocket_leave(KcSocket *sock, const char *addr, const char *ifname); +static void KcSocket_set_ifname(KcSocket *sock, const char *v4_ifname, const char *v6_ifname); +static void KcSocket_print_info(KcSocket *sock, char *buff, size_t size); +// 内部関数 static bool KcSocket_addrinfo_bind_and_connect( KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo); static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd); +static int KcSocket_join_ipv4(KcSocket *sock, const char *addr, const char *ifname); +static int KcSocket_join_ipv6(KcSocket *sock, const char *addr, const char *ifname); +static int KcSocket_leave_ipv4(KcSocket *sock, const char *addr, const char *ifname); +static int KcSocket_leave_ipv6(KcSocket *sock, const char *addr, const char *ifname); +static bool KcSocket_set_mcastif(KcSocket *sock); +static int KcSocket_set_mcastif_ipv4(KcSocket *sock, const char *ifname); +static int KcSocket_set_mcastif_ipv6(KcSocket *sock, const char *ifname); +static int KcSocket_print_addr(char *buff, size_t size, const struct sockaddr *addr, size_t addrlen); /** * ソケットのセットアップをします。 @@ -145,6 +151,8 @@ sock->set_ttl = KcSocket_set_ttl; sock->join = KcSocket_join; sock->leave = KcSocket_leave; + sock->set_ifname = KcSocket_set_ifname; + sock->print_info = KcSocket_print_info; sock->_info = (sock + 1); KcSocketInfo *info = (KcSocketInfo *)sock->_info; info->sock_fd = INVALID_SOCKET; @@ -152,6 +160,10 @@ info->sock_family = family; info->remote_addrlen = 0; info->local_addrlen = 0; + info->tmp_local_host[0] = '\0'; + info->tmp_remote_host[0] = '\0'; + info->mcast_v4_ifname = NULL; + info->mcast_v6_ifname = NULL; } return sock; } @@ -178,15 +190,13 @@ // get_remote_addr // ============================================================================= /** - * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * ソケットの接続先アドレスを返します。 + * アドレスが取得できない場合(未接続状態など)、NULL を返します。 * * @param sock 対象ソケット - * @param addr 接続先アドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットの接続先アドレス */ -static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen) +static const char *KcSocket_get_remote_addr(KcSocket *sock) { KcSocketInfo *info = (KcSocketInfo *)sock->_info; if (info->remote_addrlen) @@ -194,10 +204,10 @@ int ret = getnameinfo( (const struct sockaddr *)&info->remote_addr, info->remote_addrlen, - addr, addrlen, NULL, 0, NI_NUMERICHOST); + info->tmp_remote_host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (ret == 0) { - return addr; + return info->tmp_remote_host; } } return NULL; @@ -229,26 +239,24 @@ } /** - * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * ソケットのローカルアドレスを返します。 + * アドレスが取得できない場合、NULL を返します。 * * @param sock 対象ソケット - * @param addr ローカルアドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットのローカルアドレス */ -static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen) +static const char *KcSocket_get_local_addr(KcSocket *sock) { KcSocketInfo *info = (KcSocketInfo *)sock->_info; if (info->local_addrlen) { int ret = getnameinfo( (const struct sockaddr *)&info->local_addr, - info->remote_addrlen, - addr, addrlen, NULL, 0, NI_NUMERICHOST); + info->local_addrlen, + info->tmp_local_host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (ret == 0) { - return addr; + return info->tmp_local_host; } } return NULL; @@ -269,7 +277,7 @@ char tmp_port[NI_MAXSERV]; int ret = getnameinfo( (const struct sockaddr *)&info->local_addr, - info->remote_addrlen, + info->local_addrlen, NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); if (ret == 0) { @@ -483,8 +491,7 @@ static bool KcSocket_close(KcSocket *sock) { KcSocketInfo *info = (KcSocketInfo *)sock->_info; - - if (info->sock_family == SOCK_STREAM) + if (info->sock_type == SOCK_STREAM) { // TCP の場合は、出力を閉じて、届いているデータを読み取ってから close する。 int ret = shutdown(info->sock_fd, SHUT_WR); if (ret == 0) @@ -556,17 +563,49 @@ * @return 送信メッセージサイズ、エラー発生時は -1 */ static ssize_t KcSocket_sendto( - KcSocket *sock, - const char *buff, size_t size, int flags, + KcSocket *sock, const char *buff, size_t size, int flags, const char *addr, const char *service) { - (void)sock; - (void)buff; - (void)size; - (void)flags; - (void)addr; - (void)service; - return 0; + // 接続先アドレス情報取得 + struct addrinfo *conn_addr = NULL; + if (!sock->get_addrinfo(sock, &conn_addr, addr, service, false)) + { // 接続先情報取得NG + return SOCKET_ERROR; + } + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + socket_t tmp_sock = info->sock_fd; + ssize_t send_size = SOCKET_ERROR; + for (struct addrinfo *rp = conn_addr; rp != NULL; rp = rp->ai_next) + { + if (info->sock_fd == INVALID_SOCKET) + { // sockfd が無効の場合、ソケットを生成する。 + tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + } + + // マルチキャスト用 ifname が指定されている場合は設定する。 + KcSocket_set_mcastif(sock); + + send_size = sendto(tmp_sock, buff, size, flags, rp->ai_addr, rp->ai_addrlen); + if (send_size != SOCKET_ERROR) + { // 送信成功 + info->sock_fd = tmp_sock; + info->sock_family = rp->ai_family; + info->remote_addrlen = rp->ai_addrlen; + memcpy(&info->remote_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + else + { // 送信失敗のため、一時的に生成したソケットをクローズする。 + sockclose(tmp_sock); + } + } + freeaddrinfo(conn_addr); + return send_size; } /** @@ -582,7 +621,7 @@ * @param src_addrlen 送信元アドレス格納用バッファサイズ * @param src_service 送信元サービス格納用バッファ * @param src_servicelen 送信元サービス格納用バッファサイズ - * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + * @return 受信メッセージサイズ、エラー発生時は -1 */ static ssize_t KcSocket_recvfrom( KcSocket *sock, @@ -590,15 +629,29 @@ char *src_addr, size_t src_addrlen, char *src_service, size_t src_servicelen) { - (void)sock; - (void)buff; - (void)size; - (void)flags; - (void)src_addr; - (void)src_addrlen; - (void)src_service; - (void)src_servicelen; - return 0; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t recv_size = SOCKET_ERROR; + if (info->sock_fd != INVALID_SOCKET) + { + struct sockaddr_storage src_sockaddr; + socklen_t src_sockaddrlen = sizeof(struct sockaddr_storage); + recv_size = recvfrom( + info->sock_fd, buff, size, flags, (struct sockaddr *)&src_sockaddr, &src_sockaddrlen); + + if ((src_addr != NULL) && (src_service != NULL)) + { + int ret = getnameinfo( + (struct sockaddr *)&src_sockaddr, src_addrlen, + src_addr, src_addrlen, src_service, src_servicelen, + (NI_NUMERICHOST | NI_NUMERICSERV)); + if (ret != 0) + { + src_addr[0] = '\0'; + src_service[0] = '\0'; + } + } + } + return recv_size; } /** @@ -610,28 +663,55 @@ */ static bool KcSocket_set_ttl(KcSocket *sock, int val) { - (void)sock; - (void)val; - return true; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = SOCKET_ERROR; + switch (info->sock_family) + { + case AF_INET: + unsigned char ttl; + ttl = (unsigned char)val; + ret = setsockopt(info->sock_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); + break; + case AF_INET6: + int hop; + hop = val; + ret = setsockopt(info->sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hop, sizeof(hop)); + break; + default: + // unspoorted socket family + break; + } + return (ret == 0); } /** * マルチキャストグループに参加するために JOIN します。 + * IPv4 の場合、ifname に、IPアドレスを指定ください。 + * IPv6 の場合、ifname にインタフェース名 (例: eth0 など)を指定ください。 + * ifname に NULL が指定された場合、任意のインタフェースとなります。 * * @param sock 対象ソケット * @param addr マルチキャストアドレス * @param ifname インタフェース名 - * @param ifindex インタフェースインデックス * @return true/false (成功/失敗) */ -static bool KcSocket_join( - KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex) +static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname) { - (void)sock; - (void)addr; - (void)ifname; - (void)ifindex; - return true; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = SOCKET_ERROR; + switch (info->sock_family) + { + case AF_INET: + ret = KcSocket_join_ipv4(sock, addr, ifname); + break; + case AF_INET6: + ret = KcSocket_join_ipv6(sock, addr, ifname); + break; + default: + // NOP + break; + } + return (ret == 0); } /** @@ -639,14 +719,63 @@ * * @param sock 対象ソケット * @param addr マルチキャストアドレス + * @param ifname インタフェース名 * @return true/false (成功/失敗) */ -static bool KcSocket_leave( - KcSocket *sock, const char *addr) +static bool KcSocket_leave(KcSocket *sock, const char *addr, const char *ifname) { - (void)sock; - (void)addr; - return true; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = SOCKET_ERROR; + switch (info->sock_family) + { + case AF_INET: + ret = KcSocket_leave_ipv4(sock, addr, ifname); + break; + case AF_INET6: + ret = KcSocket_leave_ipv6(sock, addr, ifname); + break; + default: + // NOP + break; + } + return (ret == 0); +} + +/** + * マルチキャストを送信するインタフェースを指定します。 + * + * @param sock 対象ソケット + * @param v4_ifname インタフェース名(IPアドレス) + * @param v6_ifname インタフェース名(eth0 など) + * @return true/false (成功/失敗) + */ +static void KcSocket_set_ifname(KcSocket *sock, const char *v4_ifname, const char *v6_ifname) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->mcast_v4_ifname = v4_ifname; + info->mcast_v6_ifname = v6_ifname; +} + +/** + * 指定されたソケットの情報を指定されたバッファに出力します。 + * + * @param sock 対象ソケット + * @param buff バッファ + * @param size バッファのサイズ + */ +static void KcSocket_print_info(KcSocket *sock, char *buff, size_t size) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + char *write_ptr = buff; + char *end_ptr = &buff[size]; + write_ptr += snprintf(write_ptr, (end_ptr - write_ptr), "local : "); + write_ptr += KcSocket_print_addr(write_ptr, (end_ptr - write_ptr), (struct sockaddr *)&info->local_addr, info->local_addrlen); + write_ptr += snprintf(write_ptr, (end_ptr - write_ptr), "\n"); + write_ptr += snprintf(write_ptr, (end_ptr - write_ptr), "remote: "); + write_ptr += KcSocket_print_addr(write_ptr, (end_ptr - write_ptr), (struct sockaddr *)&info->remote_addr, info->remote_addrlen); + write_ptr += snprintf(write_ptr, (end_ptr - write_ptr), "\n"); + UNUSED_VARIABLE(write_ptr); } //////////////////////////////////////////////////////////////////////////////// @@ -684,12 +813,13 @@ { KcSocketInfo *info = (KcSocketInfo *)sock->_info; info->sock_fd = tmp_sock; + info->sock_family = rp->ai_family; info->remote_addrlen = rp->ai_addrlen; memcpy(&info->remote_addr, rp->ai_addr, rp->ai_addrlen); break; } - if (sockfd != INVALID_SOCKET) + if (sockfd == INVALID_SOCKET) { // sockfd が無効の場合、一時的に生成したソケットをクローズする。 sockclose(tmp_sock); } @@ -724,7 +854,7 @@ const int on = 1; setsockopt(tmp_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); ret = bind(tmp_sock, rp->ai_addr, rp->ai_addrlen); - is_success = (ret != SOCKET_ERROR); + is_success = (ret == 0); if (!is_success) { // bind 失敗したので次へ sockclose(tmp_sock); @@ -745,9 +875,180 @@ // bind または、bind と connect 成功 KcSocketInfo *info = (KcSocketInfo *)sock->_info; info->sock_fd = tmp_sock; + info->sock_family = rp->ai_family; info->local_addrlen = rp->ai_addrlen; memcpy(&info->local_addr, rp->ai_addr, rp->ai_addrlen); break; } return is_success; } + +/** + * マルチキャストグループに参加するために JOIN (IPv4)します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名(IP) + * @return 0/-1(成功/失敗) + */ +static int KcSocket_join_ipv4(KcSocket *sock, const char *addr, const char *ifname) +{ + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = inet_addr(addr); + mreq.imr_interface.s_addr = (ifname != NULL) ? inet_addr(ifname) : htonl(INADDR_ANY); + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = setsockopt(info->sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + return ret; +} + +/** + * マルチキャストグループに参加するために JOIN (IPv6)します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_join_ipv6(KcSocket *sock, const char *addr, const char *ifname) +{ + struct ipv6_mreq mreq6; + mreq6.ipv6mr_interface = (ifname != NULL) ? if_nametoindex(ifname) : 0; + int ret = inet_pton(AF_INET6, addr, &mreq6.ipv6mr_multiaddr); + if (ret != SOCKET_ERROR) + { + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ret = setsockopt(info->sock_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6)); + } + return ret; +} + +/** + * マルチキャストグループから離脱します(IPv4)。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名(IP) + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_leave_ipv4(KcSocket *sock, const char *addr, const char *ifname) +{ + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = inet_addr(addr); + mreq.imr_interface.s_addr = (ifname != NULL) ? inet_addr(ifname) : htonl(INADDR_ANY); + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = setsockopt(info->sock_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); + return ret; +} + +/** + * マルチキャストグループに離脱します(IPv6)。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_leave_ipv6(KcSocket *sock, const char *addr, const char *ifname) +{ + struct ipv6_mreq mreq6; + mreq6.ipv6mr_interface = (ifname != NULL) ? if_nametoindex(ifname) : 0; + int ret = inet_pton(AF_INET6, addr, &mreq6.ipv6mr_multiaddr); + if (ret != SOCKET_ERROR) + { + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ret = setsockopt(info->sock_fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq6, sizeof(mreq6)); + } + return ret; +} + +/** + * マルチキャストを送信するためのインタフェースを設定します。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ +static bool KcSocket_set_mcastif(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = SOCKET_ERROR; + switch (info->sock_family) + { + case AF_INET: + ret = (info->mcast_v4_ifname) + ? KcSocket_set_mcastif_ipv4(sock, info->mcast_v4_ifname) + : 0; + break; + case AF_INET6: + ret = (info->mcast_v6_ifname) + ? KcSocket_set_mcastif_ipv6(sock, info->mcast_v6_ifname) + : 0; + break; + default: + // NOP + break; + } + return (ret == 0); +} + +/** + * マルチキャストを送信するためのインタフェースを設定します(IPv4)。 + * + * @param sock 対象ソケット + * @param ifname インタフェース名 + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_set_mcastif_ipv4(KcSocket *sock, const char *ifname) +{ + struct in_addr ifaddr; + ifaddr.s_addr = (ifname != NULL) ? inet_addr(ifname) : htonl(INADDR_ANY); + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = setsockopt(info->sock_fd, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr, sizeof(ifaddr)); + return ret; +} + +/** + * マルチキャストを送信するためのインタフェースを設定します(IPv6)。 + * + * @param sock 対象ソケット + * @param ifname インタフェース名 + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_set_mcastif_ipv6(KcSocket *sock, const char *ifname) +{ + unsigned int ifindex = (ifname != NULL) ? if_nametoindex(ifname) : 0; + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = setsockopt(info->sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)); + return ret; +} + +/** + * 指定された addr の情報を出力します。 + * + * @param buff バッファ + * @param size サイズ + * @param addr アドレス + * @param addrlen アドレス長 + * @return バッファに書き出したバイト数 + */ +static int KcSocket_print_addr(char *buff, size_t size, const struct sockaddr *addr, size_t addrlen) +{ + char tmp_addr[NI_MAXHOST]; + char tmp_service[NI_MAXSERV]; + int ret = getnameinfo( + addr, addrlen, + tmp_addr, sizeof(tmp_addr), tmp_service, sizeof(tmp_service), NI_NUMERICHOST | NI_NUMERICSERV); + int write_size = 0; + if (ret == 0) + { + write_size = snprintf(buff, size, "%s:%s", tmp_addr, tmp_service); + } + else + { + write_size = snprintf(buff, size, "-:-"); + } + return write_size; +} diff --git a/modules/test/src/test_memory_entry.c b/modules/test/src/test_memory_entry.c index e883e07..b21d089 100644 --- a/modules/test/src/test_memory_entry.c +++ b/modules/test/src/test_memory_entry.c @@ -96,6 +96,6 @@ */ static void test_memory_entry_set_null(void) { - KcMemoryEntry_set(NULL, - 10, KC_MEMORY_ALLOCATED, "test_file", "test_func", 123); + KcMemoryEntry_set( + NULL, 10, KC_MEMORY_ALLOCATED, "test_file", "test_func", 123); } diff --git a/modules/test/src/test_socket.c b/modules/test/src/test_socket.c index 356882e..fdcefc3 100644 --- a/modules/test/src/test_socket.c +++ b/modules/test/src/test_socket.c @@ -12,7 +12,12 @@ // プロトタイプ宣言 static void test_socket_new(void); +static void test_socket_dgram1(void); +static void test_socket_stream1(void); +static void test_socket_stream2(void); +static void test_socket_mcast1_ipv4(void); +// テスト用変数 /** * KcSocket 単体テストスイート */ @@ -20,6 +25,108 @@ { KcUt *ut = KcUt_get_instance(); ut->add(ut, UT_TESTCASE, "socket new/delete", test_socket_new); + ut->add(ut, UT_TESTCASE, "socket dgram1", test_socket_dgram1); + ut->add(ut, UT_TESTCASE, "socket stream1", test_socket_stream1); + ut->add(ut, UT_TESTCASE, "socket stream2", test_socket_stream2); + ut->add(ut, UT_TESTCASE, "socket mcast1", test_socket_mcast1_ipv4); +} + +typedef struct +{ + int type; + int family; + const char *addr; + const char *service; + const char *mcast_addr; + const char *mcast_iface; + int exp_recv_size; + const char *exp_recv_data; + const char *exp_src_addr; + const char *exp_src_service; +} TestSocketInfo; + +static int test_socket_thread_echo_server(void *arg) +{ + TestSocketInfo *info = (TestSocketInfo *)arg; + KcSocket *server = KcSocket_new(info->type, info->family); + assert_not_null(server); + + bool ret = server->bind(server, info->addr, info->service); + assert_true(ret); + + // マルチキャスト + if (info->mcast_addr != NULL) + { + server->join(server, info->mcast_addr, info->mcast_iface); + } + + char recv_buff[16] = {'\0'}; + char src_addr[NI_MAXHOST]; + char src_service[NI_MAXSERV]; + + // メッセージ受信 + int recv_size; + KcSocket *connected_socket = NULL; + if (info->type == SOCK_STREAM) + { // TCP + ret = server->listen(server, 1); + assert_true(ret); + + connected_socket = server->accept(server); + assert_not_null(connected_socket); + + const char *raddr = connected_socket->get_remote_addr(connected_socket); + assert_not_null(raddr); + snprintf(src_addr, NI_MAXHOST, "%s", raddr); + int rport = connected_socket->get_remote_port(connected_socket); + assert_true(rport != 0); + snprintf(src_service, NI_MAXSERV, "%d", rport); + + recv_size = connected_socket->recv(connected_socket, recv_buff, sizeof(recv_buff), 0); + } + else + { // UDP + recv_size = server->recvfrom( + server, recv_buff, sizeof(recv_buff), 0, + src_addr, NI_MAXHOST, src_service, NI_MAXSERV); + printf("recv [%s] (size=%d)\n", recv_buff, recv_size); + } + + assert_equals(info->exp_recv_size, recv_size); + assert_equals(info->exp_recv_data, recv_buff); + + if (info->exp_src_addr) + { + assert_equals(info->exp_src_addr, src_addr); + } + if (info->exp_src_service) + { + assert_equals(info->exp_src_service, src_service); + } + + // 応答送信 + int write_size; + if (info->type == SOCK_STREAM) + { // TCP + write_size = connected_socket->send(connected_socket, recv_buff, recv_size, 0); + ret = connected_socket->close(connected_socket); + assert_true(ret); + KcSocket_delete(connected_socket); + } + else + { // UDP + printf("sendto: %s:%s\n", src_addr, src_service); + printf(" data: %s (size=%d)\n", recv_buff, recv_size); + write_size = server->sendto(server, recv_buff, recv_size, 0, src_addr, src_service); + if (info->mcast_addr != NULL) + { + server->leave(server, info->mcast_addr, info->mcast_iface); + } + } + assert_equals(info->exp_recv_size, write_size); + + KcSocket_delete(server); + return 0; } /** @@ -28,25 +135,303 @@ * @process KcSocket_new を実行する。。 * @result KcSocket が生成されること。 * - * @process KcSocket_delete にて Queue を破棄する。 + * @process KcSocket_delete にて Socket を破棄する。 * @result Socket が破棄されること。 */ static void test_socket_new(void) { - KcSocket *server = KcSocket_new(SOCK_STREAM, AF_UNSPEC); - assert_not_null(server); + // 受信側 + KcThread *server = KcThread_new(test_socket_thread_echo_server); + TestSocketInfo info = { + .type = SOCK_DGRAM, + .family = AF_UNSPEC, + .addr = "127.0.0.1", + .service = "5000", + .mcast_addr = NULL, + .mcast_iface = NULL, + .exp_recv_size = 4, + .exp_recv_data = "ABC", + .exp_src_addr = NULL, + .exp_src_service = NULL}; + server->start(server, &info); - KcSocket *client = KcSocket_new(SOCK_STREAM, AF_UNSPEC); - assert_not_null(server); + KcThread_msleep(100, false); - bool ret = server->bind(server, "127.0.0.1", "5000"); - assert_true(ret); - ret = server->listen(server, 1); + // 送信側 + KcSocket *client = KcSocket_new(SOCK_DGRAM, AF_UNSPEC); + assert_not_null(client); + + // メッセージ送信 + int send_size = client->sendto(client, info.exp_recv_data, info.exp_recv_size, 0, info.addr, info.service); + assert_equals(info.exp_recv_size, send_size); + + // ソケット取得 + socket_t sock = client->get_socket(client); + assert_true(sock != INVALID_SOCKET); + + // タイプ確認 + int type = client->get_type(client); + assert_equals(SOCK_DGRAM, type); + + // ファミリー確認 (IPv4 [AF_INET]) + int family = client->get_family(client); + assert_equals(AF_INET, family); + + // 応答受信 + char buff[16]; + int recv_size = client->recvfrom(client, buff, sizeof(buff), 0, NULL, 0, NULL, 0); + assert_equals(info.exp_recv_size, recv_size); + assert_equals(info.exp_recv_data, buff); + bool ret = client->close(client); assert_true(ret); - ret = client->connect(client, "127.0.0.1", "5000", NULL, NULL); - assert_true(ret); - - KcSocket_delete(server); KcSocket_delete(client); + + server->join(server); + KcThread_delete(server); +} + +/** + * UDP 送受信 + */ +static void test_socket_dgram1(void) +{ + // 受信側 + KcThread *server = KcThread_new(test_socket_thread_echo_server); + TestSocketInfo info = { + .type = SOCK_DGRAM, + .family = AF_UNSPEC, + .addr = "127.0.0.1", + .service = "5000", + .mcast_addr = NULL, + .mcast_iface = NULL, + .exp_recv_size = 5, + .exp_recv_data = "DEFG", + .exp_src_addr = "127.0.0.1", + .exp_src_service = "6000"}; + server->start(server, &info); + + KcThread_msleep(100, false); + + // 送信側 + KcSocket *client = KcSocket_new(SOCK_DGRAM, AF_UNSPEC); + assert_not_null(client); + + bool can_bind = client->bind(client, info.exp_src_addr, info.exp_src_service); + assert_true(can_bind); + + const char *local_addr = client->get_local_addr(client); + assert_equals(info.exp_src_addr, local_addr); + int port = client->get_local_port(client); + assert_equals(atoi(info.exp_src_service), port); + + char buff[1024]; + client->print_info(client, buff, sizeof(buff)); + assert_equals("local : 127.0.0.1:6000\nremote: -:-\n", buff); + + int send_size = client->sendto(client, info.exp_recv_data, info.exp_recv_size, 0, info.addr, info.service); + assert_equals(info.exp_recv_size, send_size); + + // ソケット取得 + socket_t sock = client->get_socket(client); + assert_true(sock != INVALID_SOCKET); + + // タイプ確認 + int type = client->get_type(client); + assert_equals(SOCK_DGRAM, type); + + // ファミリー確認 (IPv4 [AF_INET]) + int family = client->get_family(client); + assert_equals(AF_INET, family); + + // 応答受信 + int recv_size = client->recvfrom(client, buff, sizeof(buff), 0, NULL, 0, NULL, 0); + assert_equals(info.exp_recv_size, recv_size); + assert_equals(info.exp_recv_data, buff); + bool ret = client->close(client); + assert_true(ret); + + KcSocket_delete(client); + + server->join(server); + KcThread_delete(server); +} + +/** + * TCP 送受信 + */ +static void test_socket_stream1(void) +{ + // 受信側 + KcThread *server = KcThread_new(test_socket_thread_echo_server); + TestSocketInfo info = { + .type = SOCK_STREAM, + .family = AF_UNSPEC, + .addr = "127.0.0.1", + .service = "5000", + .mcast_addr = NULL, + .mcast_iface = NULL, + .exp_recv_size = 4, + .exp_recv_data = "XYZ", + .exp_src_addr = NULL, + .exp_src_service = NULL}; + server->start(server, &info); + + KcThread_msleep(100, false); + + // 送信側 + KcSocket *client = KcSocket_new(SOCK_STREAM, AF_UNSPEC); + assert_not_null(client); + + bool ret = client->connect(client, info.addr, info.service, NULL, NULL); + assert_true(ret); + + // ソケット取得 + socket_t sock = client->get_socket(client); + assert_true(sock != INVALID_SOCKET); + + // タイプ確認 + int type = client->get_type(client); + assert_equals(SOCK_STREAM, type); + + // ファミリー確認 (IPv4 [AF_INET]) + int family = client->get_family(client); + assert_equals(AF_INET, family); + + int send_size = client->send(client, info.exp_recv_data, info.exp_recv_size, 0); + assert_equals(4, send_size); + + // 応答受信 + char buff[16]; + int recv_size = client->recv(client, buff, sizeof(buff), 0); + assert_equals(info.exp_recv_size, recv_size); + ret = client->close(client); + assert_true(ret); + + KcSocket_delete(client); + + server->join(server); + KcThread_delete(server); +} + +/** + * TCP 送受信 + */ +static void test_socket_stream2(void) +{ + // 受信側 + KcThread *server = KcThread_new(test_socket_thread_echo_server); + TestSocketInfo info = { + .type = SOCK_STREAM, + .family = AF_UNSPEC, + .addr = "127.0.0.1", + .service = "5000", + .mcast_addr = NULL, + .mcast_iface = NULL, + .exp_recv_size = 4, + .exp_recv_data = "XYZ", + .exp_src_addr = "127.0.0.1", + .exp_src_service = "6000"}; + server->start(server, &info); + + KcThread_msleep(100, false); + + // 送信側 + KcSocket *client = KcSocket_new(SOCK_STREAM, AF_UNSPEC); + assert_not_null(client); + + bool ret = client->connect(client, info.addr, info.service, info.exp_src_addr, info.exp_src_service); + assert_true(ret); + + // ソケット取得 + socket_t sock = client->get_socket(client); + assert_true(sock != INVALID_SOCKET); + + // タイプ確認 + int type = client->get_type(client); + assert_equals(SOCK_STREAM, type); + + // ファミリー確認 (IPv4 [AF_INET]) + int family = client->get_family(client); + assert_equals(AF_INET, family); + + const char *local_addr = client->get_local_addr(client); + assert_equals(info.exp_src_addr, local_addr); + int port = client->get_local_port(client); + assert_equals(atoi(info.exp_src_service), port); + + char buff[1024]; + client->print_info(client, buff, sizeof(buff)); + assert_equals("local : 127.0.0.1:6000\nremote: 127.0.0.1:5000\n", buff); + + int send_size = client->send(client, info.exp_recv_data, info.exp_recv_size, 0); + assert_equals(4, send_size); + + // 応答受信 + int recv_size = client->recv(client, buff, sizeof(buff), 0); + assert_equals(info.exp_recv_size, recv_size); + printf("buff=%s\n", buff); + ret = client->close(client); + assert_true(ret); + + KcSocket_delete(client); + + server->join(server); + KcThread_delete(server); +} + +/** + * マルチキャスト送受信 + */ +static void test_socket_mcast1_ipv4(void) +{ + // 受信側 + KcThread *server = KcThread_new(test_socket_thread_echo_server); + TestSocketInfo info = { + .type = SOCK_DGRAM, + .family = AF_UNSPEC, + .addr = "0.0.0.0", + .service = "5000", + .mcast_addr = "224.0.0.10", + .mcast_iface = NULL, + .exp_recv_size = 6, + .exp_recv_data = "AAAAA", + .exp_src_addr = NULL, + .exp_src_service = NULL}; + server->start(server, &info); + + KcThread_msleep(100, false); + + // 送信側 + KcSocket *client = KcSocket_new(SOCK_DGRAM, AF_UNSPEC); + assert_not_null(client); + + client->set_ifname(client, "10.0.2.15", NULL); + int send_size = client->sendto(client, info.exp_recv_data, info.exp_recv_size, 0, info.mcast_addr, info.service); + assert_equals(info.exp_recv_size, send_size); + + // ソケット取得 + socket_t sock = client->get_socket(client); + assert_true(sock != INVALID_SOCKET); + + // タイプ確認 + int type = client->get_type(client); + assert_equals(SOCK_DGRAM, type); + + // ファミリー確認 (IPv4 [AF_INET]) + int family = client->get_family(client); + assert_equals(AF_INET, family); + + // 応答受信 + char buff[16]; + int recv_size = client->recvfrom(client, buff, sizeof(buff), 0, NULL, 0, NULL, 0); + assert_equals(info.exp_recv_size, recv_size); + assert_equals((char *)info.exp_recv_data, buff); + bool ret = client->close(client); + assert_true(ret); + + KcSocket_delete(client); + + server->join(server); + KcThread_delete(server); } diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h index 7d952c7..f9b28ec 100644 --- a/modules/include/kc_socket.h +++ b/modules/include/kc_socket.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -42,15 +43,13 @@ { /** - * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * ソケットの接続先アドレスを返します。 + * アドレスが取得できない場合(未接続状態など)、NULL を返します。 * * @param sock 対象ソケット - * @param addr 接続先アドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットの接続先アドレス */ - const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + const char *(*get_remote_addr)(struct KcSocket_ *sock); /** * ソケットの接続先ポート番号を返します。 @@ -62,15 +61,13 @@ int (*get_remote_port)(struct KcSocket_ *sock); /** - * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * ソケットのローカルアドレスを返します。 + * アドレスが取得できない場合、NULL を返します。 * * @param sock 対象ソケット - * @param addr ローカルアドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットのローカルアドレス */ - const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + const char *(*get_local_addr)(struct KcSocket_ *sock); /** * ソケットのローカルポート番号を返します。 @@ -227,7 +224,7 @@ * @param src_addrlen 送信元アドレス格納用バッファサイズ * @param src_service 送信元サービス格納用バッファ * @param src_servicelen 送信元サービス格納用バッファサイズ - * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + * @return 受信メッセージサイズ、エラー発生時は -1 */ ssize_t (*recvfrom)( struct KcSocket_ *sock, @@ -246,23 +243,45 @@ /** * マルチキャストグループに参加するために JOIN します。 + * IPv4 の場合、ifname に、IPアドレスを指定ください。 + * IPv6 の場合、ifname にインタフェース名 (例: eth0 など)を指定ください。 + * ifname に NULL が指定された場合、任意のインタフェースとなります。 * * @param sock 対象ソケット * @param addr マルチキャストアドレス * @param ifname インタフェース名 - * @param ifindex インタフェースインデックス * @return true/false (成功/失敗) */ - bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname); /** * マルチキャストグループから離脱します。 * * @param sock 対象ソケット * @param addr マルチキャストアドレス + * @param ifname インタフェース名 * @return true/false (成功/失敗) */ - bool (*leave)(struct KcSocket_ *sock, const char *addr); + bool (*leave)(struct KcSocket_ *sock, const char *addr, const char *ifname); + + /** + * マルチキャストを送信するインタフェースを指定します。 + * + * @param sock 対象ソケット + * @param v4_ifname インタフェース名(IPアドレス) + * @param v6_ifname インタフェース名(eth0 など) + * @return true/false (成功/失敗) + */ + void (*set_ifname)(struct KcSocket_ *sock, const char *v4_ifname, const char *v6_ifname); + + /** + * 指定されたソケットの情報を指定されたバッファに出力します。 + * + * @param buff バッファ + * @param size バッファサイズ + * @param sock 対象ソケット + */ + void (*print_info)(struct KcSocket_ *sock, char *buff, size_t size); /** * Socket 管理情報 diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 7cd4ecc..402a158 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -87,6 +87,12 @@ snprintf(assert_message, sizeof(assert_message), "%s:%d: %s: Assertion expected <%s> but was <%s>", file, line, func, expected, actual); + if (strlen(actual) == 6) + { + printf("[%s]\n", actual); + printf("[%c][%02x]\n", actual[5], actual[5]); + } + assert_handler(assert_message); } } diff --git a/modules/src/kc_socket.c b/modules/src/kc_socket.c index cb67996..f957c05 100644 --- a/modules/src/kc_socket.c +++ b/modules/src/kc_socket.c @@ -3,6 +3,8 @@ * @brief ソケットモジュール * @copyright 2003 - 2024 Nomura Kei */ +#include + #include #include @@ -22,7 +24,11 @@ struct sockaddr_storage remote_addr; //!< リモートアドレス struct sockaddr_storage local_addr; //!< ローカルアドレス socklen_t remote_addrlen; //!< リモートアドレス長 - socklen_t local_addrlen; //!< ローカルアドレス帳 + socklen_t local_addrlen; //!< ローカルアドレス長 + char tmp_remote_host[NI_MAXHOST]; //!< 一時的なリモートホスト名格納用 + char tmp_local_host[NI_MAXHOST]; //!< 一時的なローカルホスト名格納用 + const char *mcast_v4_ifname; //!< マルチキャスト ifname (IPv4) + const char *mcast_v6_ifname; //!< マルチキャスト ifname (IPv6) } KcSocketInfo; /** @@ -39,41 +45,41 @@ // ============================================================================= static void Socket_setup(void); static void Socket_cleanup(void); -static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen); +static const char *KcSocket_get_remote_addr(KcSocket *sock); static int KcSocket_get_remote_port(KcSocket *sock); -static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen); +static const char *KcSocket_get_local_addr(KcSocket *sock); static int KcSocket_get_local_port(KcSocket *sock); static socket_t KcSocket_get_socket(KcSocket *sock); static int KcSocket_get_type(KcSocket *sock); static int KcSocket_get_family(KcSocket *sock); -static bool KcSocket_get_addrinfo( - KcSocket *sock, struct addrinfo **result, - const char *addr, const char *service, bool is_passive); +static bool KcSocket_get_addrinfo(KcSocket *sock, struct addrinfo **result, const char *addr, const char *service, bool is_passive); static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service); static bool KcSocket_listen(KcSocket *sock, int backlog); static KcSocket *KcSocket_accept(KcSocket *sock); -static bool KcSocket_connect( - KcSocket *sock, const char *addr, const char *service, - const char *local_addr, const char *local_service); +static bool KcSocket_connect(KcSocket *sock, const char *addr, const char *service, const char *local_addr, const char *local_service); static bool KcSocket_close(KcSocket *sock); static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags); static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags); -static ssize_t KcSocket_sendto( - KcSocket *sock, - const char *buff, size_t size, int flags, - const char *addr, const char *service); -static ssize_t KcSocket_recvfrom( - KcSocket *sock, - char *buff, size_t size, int flags, - char *src_addr, size_t src_addrlen, - char *src_service, size_t src_servicelen); +static ssize_t KcSocket_sendto(KcSocket *sock, const char *buff, size_t size, int flags, const char *addr, const char *service); +static ssize_t KcSocket_recvfrom(KcSocket *sock, char *buff, size_t size, int flags, char *src_addr, size_t src_addrlen, char *src_service, size_t src_servicelen); static bool KcSocket_set_ttl(KcSocket *sock, int val); -static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex); -static bool KcSocket_leave(KcSocket *sock, const char *addr); +static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname); +static bool KcSocket_leave(KcSocket *sock, const char *addr, const char *ifname); +static void KcSocket_set_ifname(KcSocket *sock, const char *v4_ifname, const char *v6_ifname); +static void KcSocket_print_info(KcSocket *sock, char *buff, size_t size); +// 内部関数 static bool KcSocket_addrinfo_bind_and_connect( KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo); static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd); +static int KcSocket_join_ipv4(KcSocket *sock, const char *addr, const char *ifname); +static int KcSocket_join_ipv6(KcSocket *sock, const char *addr, const char *ifname); +static int KcSocket_leave_ipv4(KcSocket *sock, const char *addr, const char *ifname); +static int KcSocket_leave_ipv6(KcSocket *sock, const char *addr, const char *ifname); +static bool KcSocket_set_mcastif(KcSocket *sock); +static int KcSocket_set_mcastif_ipv4(KcSocket *sock, const char *ifname); +static int KcSocket_set_mcastif_ipv6(KcSocket *sock, const char *ifname); +static int KcSocket_print_addr(char *buff, size_t size, const struct sockaddr *addr, size_t addrlen); /** * ソケットのセットアップをします。 @@ -145,6 +151,8 @@ sock->set_ttl = KcSocket_set_ttl; sock->join = KcSocket_join; sock->leave = KcSocket_leave; + sock->set_ifname = KcSocket_set_ifname; + sock->print_info = KcSocket_print_info; sock->_info = (sock + 1); KcSocketInfo *info = (KcSocketInfo *)sock->_info; info->sock_fd = INVALID_SOCKET; @@ -152,6 +160,10 @@ info->sock_family = family; info->remote_addrlen = 0; info->local_addrlen = 0; + info->tmp_local_host[0] = '\0'; + info->tmp_remote_host[0] = '\0'; + info->mcast_v4_ifname = NULL; + info->mcast_v6_ifname = NULL; } return sock; } @@ -178,15 +190,13 @@ // get_remote_addr // ============================================================================= /** - * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * ソケットの接続先アドレスを返します。 + * アドレスが取得できない場合(未接続状態など)、NULL を返します。 * * @param sock 対象ソケット - * @param addr 接続先アドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットの接続先アドレス */ -static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen) +static const char *KcSocket_get_remote_addr(KcSocket *sock) { KcSocketInfo *info = (KcSocketInfo *)sock->_info; if (info->remote_addrlen) @@ -194,10 +204,10 @@ int ret = getnameinfo( (const struct sockaddr *)&info->remote_addr, info->remote_addrlen, - addr, addrlen, NULL, 0, NI_NUMERICHOST); + info->tmp_remote_host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (ret == 0) { - return addr; + return info->tmp_remote_host; } } return NULL; @@ -229,26 +239,24 @@ } /** - * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 - * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * ソケットのローカルアドレスを返します。 + * アドレスが取得できない場合、NULL を返します。 * * @param sock 対象ソケット - * @param addr ローカルアドレス格納用バッファ - * @param addrlen addr のバッファサイズ - * @return addr へのポインタ + * @return ソケットのローカルアドレス */ -static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen) +static const char *KcSocket_get_local_addr(KcSocket *sock) { KcSocketInfo *info = (KcSocketInfo *)sock->_info; if (info->local_addrlen) { int ret = getnameinfo( (const struct sockaddr *)&info->local_addr, - info->remote_addrlen, - addr, addrlen, NULL, 0, NI_NUMERICHOST); + info->local_addrlen, + info->tmp_local_host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (ret == 0) { - return addr; + return info->tmp_local_host; } } return NULL; @@ -269,7 +277,7 @@ char tmp_port[NI_MAXSERV]; int ret = getnameinfo( (const struct sockaddr *)&info->local_addr, - info->remote_addrlen, + info->local_addrlen, NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); if (ret == 0) { @@ -483,8 +491,7 @@ static bool KcSocket_close(KcSocket *sock) { KcSocketInfo *info = (KcSocketInfo *)sock->_info; - - if (info->sock_family == SOCK_STREAM) + if (info->sock_type == SOCK_STREAM) { // TCP の場合は、出力を閉じて、届いているデータを読み取ってから close する。 int ret = shutdown(info->sock_fd, SHUT_WR); if (ret == 0) @@ -556,17 +563,49 @@ * @return 送信メッセージサイズ、エラー発生時は -1 */ static ssize_t KcSocket_sendto( - KcSocket *sock, - const char *buff, size_t size, int flags, + KcSocket *sock, const char *buff, size_t size, int flags, const char *addr, const char *service) { - (void)sock; - (void)buff; - (void)size; - (void)flags; - (void)addr; - (void)service; - return 0; + // 接続先アドレス情報取得 + struct addrinfo *conn_addr = NULL; + if (!sock->get_addrinfo(sock, &conn_addr, addr, service, false)) + { // 接続先情報取得NG + return SOCKET_ERROR; + } + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + socket_t tmp_sock = info->sock_fd; + ssize_t send_size = SOCKET_ERROR; + for (struct addrinfo *rp = conn_addr; rp != NULL; rp = rp->ai_next) + { + if (info->sock_fd == INVALID_SOCKET) + { // sockfd が無効の場合、ソケットを生成する。 + tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + } + + // マルチキャスト用 ifname が指定されている場合は設定する。 + KcSocket_set_mcastif(sock); + + send_size = sendto(tmp_sock, buff, size, flags, rp->ai_addr, rp->ai_addrlen); + if (send_size != SOCKET_ERROR) + { // 送信成功 + info->sock_fd = tmp_sock; + info->sock_family = rp->ai_family; + info->remote_addrlen = rp->ai_addrlen; + memcpy(&info->remote_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + else + { // 送信失敗のため、一時的に生成したソケットをクローズする。 + sockclose(tmp_sock); + } + } + freeaddrinfo(conn_addr); + return send_size; } /** @@ -582,7 +621,7 @@ * @param src_addrlen 送信元アドレス格納用バッファサイズ * @param src_service 送信元サービス格納用バッファ * @param src_servicelen 送信元サービス格納用バッファサイズ - * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + * @return 受信メッセージサイズ、エラー発生時は -1 */ static ssize_t KcSocket_recvfrom( KcSocket *sock, @@ -590,15 +629,29 @@ char *src_addr, size_t src_addrlen, char *src_service, size_t src_servicelen) { - (void)sock; - (void)buff; - (void)size; - (void)flags; - (void)src_addr; - (void)src_addrlen; - (void)src_service; - (void)src_servicelen; - return 0; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t recv_size = SOCKET_ERROR; + if (info->sock_fd != INVALID_SOCKET) + { + struct sockaddr_storage src_sockaddr; + socklen_t src_sockaddrlen = sizeof(struct sockaddr_storage); + recv_size = recvfrom( + info->sock_fd, buff, size, flags, (struct sockaddr *)&src_sockaddr, &src_sockaddrlen); + + if ((src_addr != NULL) && (src_service != NULL)) + { + int ret = getnameinfo( + (struct sockaddr *)&src_sockaddr, src_addrlen, + src_addr, src_addrlen, src_service, src_servicelen, + (NI_NUMERICHOST | NI_NUMERICSERV)); + if (ret != 0) + { + src_addr[0] = '\0'; + src_service[0] = '\0'; + } + } + } + return recv_size; } /** @@ -610,28 +663,55 @@ */ static bool KcSocket_set_ttl(KcSocket *sock, int val) { - (void)sock; - (void)val; - return true; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = SOCKET_ERROR; + switch (info->sock_family) + { + case AF_INET: + unsigned char ttl; + ttl = (unsigned char)val; + ret = setsockopt(info->sock_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); + break; + case AF_INET6: + int hop; + hop = val; + ret = setsockopt(info->sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hop, sizeof(hop)); + break; + default: + // unspoorted socket family + break; + } + return (ret == 0); } /** * マルチキャストグループに参加するために JOIN します。 + * IPv4 の場合、ifname に、IPアドレスを指定ください。 + * IPv6 の場合、ifname にインタフェース名 (例: eth0 など)を指定ください。 + * ifname に NULL が指定された場合、任意のインタフェースとなります。 * * @param sock 対象ソケット * @param addr マルチキャストアドレス * @param ifname インタフェース名 - * @param ifindex インタフェースインデックス * @return true/false (成功/失敗) */ -static bool KcSocket_join( - KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex) +static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname) { - (void)sock; - (void)addr; - (void)ifname; - (void)ifindex; - return true; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = SOCKET_ERROR; + switch (info->sock_family) + { + case AF_INET: + ret = KcSocket_join_ipv4(sock, addr, ifname); + break; + case AF_INET6: + ret = KcSocket_join_ipv6(sock, addr, ifname); + break; + default: + // NOP + break; + } + return (ret == 0); } /** @@ -639,14 +719,63 @@ * * @param sock 対象ソケット * @param addr マルチキャストアドレス + * @param ifname インタフェース名 * @return true/false (成功/失敗) */ -static bool KcSocket_leave( - KcSocket *sock, const char *addr) +static bool KcSocket_leave(KcSocket *sock, const char *addr, const char *ifname) { - (void)sock; - (void)addr; - return true; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = SOCKET_ERROR; + switch (info->sock_family) + { + case AF_INET: + ret = KcSocket_leave_ipv4(sock, addr, ifname); + break; + case AF_INET6: + ret = KcSocket_leave_ipv6(sock, addr, ifname); + break; + default: + // NOP + break; + } + return (ret == 0); +} + +/** + * マルチキャストを送信するインタフェースを指定します。 + * + * @param sock 対象ソケット + * @param v4_ifname インタフェース名(IPアドレス) + * @param v6_ifname インタフェース名(eth0 など) + * @return true/false (成功/失敗) + */ +static void KcSocket_set_ifname(KcSocket *sock, const char *v4_ifname, const char *v6_ifname) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->mcast_v4_ifname = v4_ifname; + info->mcast_v6_ifname = v6_ifname; +} + +/** + * 指定されたソケットの情報を指定されたバッファに出力します。 + * + * @param sock 対象ソケット + * @param buff バッファ + * @param size バッファのサイズ + */ +static void KcSocket_print_info(KcSocket *sock, char *buff, size_t size) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + char *write_ptr = buff; + char *end_ptr = &buff[size]; + write_ptr += snprintf(write_ptr, (end_ptr - write_ptr), "local : "); + write_ptr += KcSocket_print_addr(write_ptr, (end_ptr - write_ptr), (struct sockaddr *)&info->local_addr, info->local_addrlen); + write_ptr += snprintf(write_ptr, (end_ptr - write_ptr), "\n"); + write_ptr += snprintf(write_ptr, (end_ptr - write_ptr), "remote: "); + write_ptr += KcSocket_print_addr(write_ptr, (end_ptr - write_ptr), (struct sockaddr *)&info->remote_addr, info->remote_addrlen); + write_ptr += snprintf(write_ptr, (end_ptr - write_ptr), "\n"); + UNUSED_VARIABLE(write_ptr); } //////////////////////////////////////////////////////////////////////////////// @@ -684,12 +813,13 @@ { KcSocketInfo *info = (KcSocketInfo *)sock->_info; info->sock_fd = tmp_sock; + info->sock_family = rp->ai_family; info->remote_addrlen = rp->ai_addrlen; memcpy(&info->remote_addr, rp->ai_addr, rp->ai_addrlen); break; } - if (sockfd != INVALID_SOCKET) + if (sockfd == INVALID_SOCKET) { // sockfd が無効の場合、一時的に生成したソケットをクローズする。 sockclose(tmp_sock); } @@ -724,7 +854,7 @@ const int on = 1; setsockopt(tmp_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); ret = bind(tmp_sock, rp->ai_addr, rp->ai_addrlen); - is_success = (ret != SOCKET_ERROR); + is_success = (ret == 0); if (!is_success) { // bind 失敗したので次へ sockclose(tmp_sock); @@ -745,9 +875,180 @@ // bind または、bind と connect 成功 KcSocketInfo *info = (KcSocketInfo *)sock->_info; info->sock_fd = tmp_sock; + info->sock_family = rp->ai_family; info->local_addrlen = rp->ai_addrlen; memcpy(&info->local_addr, rp->ai_addr, rp->ai_addrlen); break; } return is_success; } + +/** + * マルチキャストグループに参加するために JOIN (IPv4)します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名(IP) + * @return 0/-1(成功/失敗) + */ +static int KcSocket_join_ipv4(KcSocket *sock, const char *addr, const char *ifname) +{ + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = inet_addr(addr); + mreq.imr_interface.s_addr = (ifname != NULL) ? inet_addr(ifname) : htonl(INADDR_ANY); + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = setsockopt(info->sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + return ret; +} + +/** + * マルチキャストグループに参加するために JOIN (IPv6)します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_join_ipv6(KcSocket *sock, const char *addr, const char *ifname) +{ + struct ipv6_mreq mreq6; + mreq6.ipv6mr_interface = (ifname != NULL) ? if_nametoindex(ifname) : 0; + int ret = inet_pton(AF_INET6, addr, &mreq6.ipv6mr_multiaddr); + if (ret != SOCKET_ERROR) + { + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ret = setsockopt(info->sock_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6)); + } + return ret; +} + +/** + * マルチキャストグループから離脱します(IPv4)。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名(IP) + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_leave_ipv4(KcSocket *sock, const char *addr, const char *ifname) +{ + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = inet_addr(addr); + mreq.imr_interface.s_addr = (ifname != NULL) ? inet_addr(ifname) : htonl(INADDR_ANY); + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = setsockopt(info->sock_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); + return ret; +} + +/** + * マルチキャストグループに離脱します(IPv6)。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_leave_ipv6(KcSocket *sock, const char *addr, const char *ifname) +{ + struct ipv6_mreq mreq6; + mreq6.ipv6mr_interface = (ifname != NULL) ? if_nametoindex(ifname) : 0; + int ret = inet_pton(AF_INET6, addr, &mreq6.ipv6mr_multiaddr); + if (ret != SOCKET_ERROR) + { + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ret = setsockopt(info->sock_fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq6, sizeof(mreq6)); + } + return ret; +} + +/** + * マルチキャストを送信するためのインタフェースを設定します。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ +static bool KcSocket_set_mcastif(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = SOCKET_ERROR; + switch (info->sock_family) + { + case AF_INET: + ret = (info->mcast_v4_ifname) + ? KcSocket_set_mcastif_ipv4(sock, info->mcast_v4_ifname) + : 0; + break; + case AF_INET6: + ret = (info->mcast_v6_ifname) + ? KcSocket_set_mcastif_ipv6(sock, info->mcast_v6_ifname) + : 0; + break; + default: + // NOP + break; + } + return (ret == 0); +} + +/** + * マルチキャストを送信するためのインタフェースを設定します(IPv4)。 + * + * @param sock 対象ソケット + * @param ifname インタフェース名 + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_set_mcastif_ipv4(KcSocket *sock, const char *ifname) +{ + struct in_addr ifaddr; + ifaddr.s_addr = (ifname != NULL) ? inet_addr(ifname) : htonl(INADDR_ANY); + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = setsockopt(info->sock_fd, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr, sizeof(ifaddr)); + return ret; +} + +/** + * マルチキャストを送信するためのインタフェースを設定します(IPv6)。 + * + * @param sock 対象ソケット + * @param ifname インタフェース名 + * @return 0/-1 (成功/失敗) + */ +static int KcSocket_set_mcastif_ipv6(KcSocket *sock, const char *ifname) +{ + unsigned int ifindex = (ifname != NULL) ? if_nametoindex(ifname) : 0; + + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int ret = setsockopt(info->sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)); + return ret; +} + +/** + * 指定された addr の情報を出力します。 + * + * @param buff バッファ + * @param size サイズ + * @param addr アドレス + * @param addrlen アドレス長 + * @return バッファに書き出したバイト数 + */ +static int KcSocket_print_addr(char *buff, size_t size, const struct sockaddr *addr, size_t addrlen) +{ + char tmp_addr[NI_MAXHOST]; + char tmp_service[NI_MAXSERV]; + int ret = getnameinfo( + addr, addrlen, + tmp_addr, sizeof(tmp_addr), tmp_service, sizeof(tmp_service), NI_NUMERICHOST | NI_NUMERICSERV); + int write_size = 0; + if (ret == 0) + { + write_size = snprintf(buff, size, "%s:%s", tmp_addr, tmp_service); + } + else + { + write_size = snprintf(buff, size, "-:-"); + } + return write_size; +} diff --git a/modules/test/src/test_memory_entry.c b/modules/test/src/test_memory_entry.c index e883e07..b21d089 100644 --- a/modules/test/src/test_memory_entry.c +++ b/modules/test/src/test_memory_entry.c @@ -96,6 +96,6 @@ */ static void test_memory_entry_set_null(void) { - KcMemoryEntry_set(NULL, - 10, KC_MEMORY_ALLOCATED, "test_file", "test_func", 123); + KcMemoryEntry_set( + NULL, 10, KC_MEMORY_ALLOCATED, "test_file", "test_func", 123); } diff --git a/modules/test/src/test_socket.c b/modules/test/src/test_socket.c index 356882e..fdcefc3 100644 --- a/modules/test/src/test_socket.c +++ b/modules/test/src/test_socket.c @@ -12,7 +12,12 @@ // プロトタイプ宣言 static void test_socket_new(void); +static void test_socket_dgram1(void); +static void test_socket_stream1(void); +static void test_socket_stream2(void); +static void test_socket_mcast1_ipv4(void); +// テスト用変数 /** * KcSocket 単体テストスイート */ @@ -20,6 +25,108 @@ { KcUt *ut = KcUt_get_instance(); ut->add(ut, UT_TESTCASE, "socket new/delete", test_socket_new); + ut->add(ut, UT_TESTCASE, "socket dgram1", test_socket_dgram1); + ut->add(ut, UT_TESTCASE, "socket stream1", test_socket_stream1); + ut->add(ut, UT_TESTCASE, "socket stream2", test_socket_stream2); + ut->add(ut, UT_TESTCASE, "socket mcast1", test_socket_mcast1_ipv4); +} + +typedef struct +{ + int type; + int family; + const char *addr; + const char *service; + const char *mcast_addr; + const char *mcast_iface; + int exp_recv_size; + const char *exp_recv_data; + const char *exp_src_addr; + const char *exp_src_service; +} TestSocketInfo; + +static int test_socket_thread_echo_server(void *arg) +{ + TestSocketInfo *info = (TestSocketInfo *)arg; + KcSocket *server = KcSocket_new(info->type, info->family); + assert_not_null(server); + + bool ret = server->bind(server, info->addr, info->service); + assert_true(ret); + + // マルチキャスト + if (info->mcast_addr != NULL) + { + server->join(server, info->mcast_addr, info->mcast_iface); + } + + char recv_buff[16] = {'\0'}; + char src_addr[NI_MAXHOST]; + char src_service[NI_MAXSERV]; + + // メッセージ受信 + int recv_size; + KcSocket *connected_socket = NULL; + if (info->type == SOCK_STREAM) + { // TCP + ret = server->listen(server, 1); + assert_true(ret); + + connected_socket = server->accept(server); + assert_not_null(connected_socket); + + const char *raddr = connected_socket->get_remote_addr(connected_socket); + assert_not_null(raddr); + snprintf(src_addr, NI_MAXHOST, "%s", raddr); + int rport = connected_socket->get_remote_port(connected_socket); + assert_true(rport != 0); + snprintf(src_service, NI_MAXSERV, "%d", rport); + + recv_size = connected_socket->recv(connected_socket, recv_buff, sizeof(recv_buff), 0); + } + else + { // UDP + recv_size = server->recvfrom( + server, recv_buff, sizeof(recv_buff), 0, + src_addr, NI_MAXHOST, src_service, NI_MAXSERV); + printf("recv [%s] (size=%d)\n", recv_buff, recv_size); + } + + assert_equals(info->exp_recv_size, recv_size); + assert_equals(info->exp_recv_data, recv_buff); + + if (info->exp_src_addr) + { + assert_equals(info->exp_src_addr, src_addr); + } + if (info->exp_src_service) + { + assert_equals(info->exp_src_service, src_service); + } + + // 応答送信 + int write_size; + if (info->type == SOCK_STREAM) + { // TCP + write_size = connected_socket->send(connected_socket, recv_buff, recv_size, 0); + ret = connected_socket->close(connected_socket); + assert_true(ret); + KcSocket_delete(connected_socket); + } + else + { // UDP + printf("sendto: %s:%s\n", src_addr, src_service); + printf(" data: %s (size=%d)\n", recv_buff, recv_size); + write_size = server->sendto(server, recv_buff, recv_size, 0, src_addr, src_service); + if (info->mcast_addr != NULL) + { + server->leave(server, info->mcast_addr, info->mcast_iface); + } + } + assert_equals(info->exp_recv_size, write_size); + + KcSocket_delete(server); + return 0; } /** @@ -28,25 +135,303 @@ * @process KcSocket_new を実行する。。 * @result KcSocket が生成されること。 * - * @process KcSocket_delete にて Queue を破棄する。 + * @process KcSocket_delete にて Socket を破棄する。 * @result Socket が破棄されること。 */ static void test_socket_new(void) { - KcSocket *server = KcSocket_new(SOCK_STREAM, AF_UNSPEC); - assert_not_null(server); + // 受信側 + KcThread *server = KcThread_new(test_socket_thread_echo_server); + TestSocketInfo info = { + .type = SOCK_DGRAM, + .family = AF_UNSPEC, + .addr = "127.0.0.1", + .service = "5000", + .mcast_addr = NULL, + .mcast_iface = NULL, + .exp_recv_size = 4, + .exp_recv_data = "ABC", + .exp_src_addr = NULL, + .exp_src_service = NULL}; + server->start(server, &info); - KcSocket *client = KcSocket_new(SOCK_STREAM, AF_UNSPEC); - assert_not_null(server); + KcThread_msleep(100, false); - bool ret = server->bind(server, "127.0.0.1", "5000"); - assert_true(ret); - ret = server->listen(server, 1); + // 送信側 + KcSocket *client = KcSocket_new(SOCK_DGRAM, AF_UNSPEC); + assert_not_null(client); + + // メッセージ送信 + int send_size = client->sendto(client, info.exp_recv_data, info.exp_recv_size, 0, info.addr, info.service); + assert_equals(info.exp_recv_size, send_size); + + // ソケット取得 + socket_t sock = client->get_socket(client); + assert_true(sock != INVALID_SOCKET); + + // タイプ確認 + int type = client->get_type(client); + assert_equals(SOCK_DGRAM, type); + + // ファミリー確認 (IPv4 [AF_INET]) + int family = client->get_family(client); + assert_equals(AF_INET, family); + + // 応答受信 + char buff[16]; + int recv_size = client->recvfrom(client, buff, sizeof(buff), 0, NULL, 0, NULL, 0); + assert_equals(info.exp_recv_size, recv_size); + assert_equals(info.exp_recv_data, buff); + bool ret = client->close(client); assert_true(ret); - ret = client->connect(client, "127.0.0.1", "5000", NULL, NULL); - assert_true(ret); - - KcSocket_delete(server); KcSocket_delete(client); + + server->join(server); + KcThread_delete(server); +} + +/** + * UDP 送受信 + */ +static void test_socket_dgram1(void) +{ + // 受信側 + KcThread *server = KcThread_new(test_socket_thread_echo_server); + TestSocketInfo info = { + .type = SOCK_DGRAM, + .family = AF_UNSPEC, + .addr = "127.0.0.1", + .service = "5000", + .mcast_addr = NULL, + .mcast_iface = NULL, + .exp_recv_size = 5, + .exp_recv_data = "DEFG", + .exp_src_addr = "127.0.0.1", + .exp_src_service = "6000"}; + server->start(server, &info); + + KcThread_msleep(100, false); + + // 送信側 + KcSocket *client = KcSocket_new(SOCK_DGRAM, AF_UNSPEC); + assert_not_null(client); + + bool can_bind = client->bind(client, info.exp_src_addr, info.exp_src_service); + assert_true(can_bind); + + const char *local_addr = client->get_local_addr(client); + assert_equals(info.exp_src_addr, local_addr); + int port = client->get_local_port(client); + assert_equals(atoi(info.exp_src_service), port); + + char buff[1024]; + client->print_info(client, buff, sizeof(buff)); + assert_equals("local : 127.0.0.1:6000\nremote: -:-\n", buff); + + int send_size = client->sendto(client, info.exp_recv_data, info.exp_recv_size, 0, info.addr, info.service); + assert_equals(info.exp_recv_size, send_size); + + // ソケット取得 + socket_t sock = client->get_socket(client); + assert_true(sock != INVALID_SOCKET); + + // タイプ確認 + int type = client->get_type(client); + assert_equals(SOCK_DGRAM, type); + + // ファミリー確認 (IPv4 [AF_INET]) + int family = client->get_family(client); + assert_equals(AF_INET, family); + + // 応答受信 + int recv_size = client->recvfrom(client, buff, sizeof(buff), 0, NULL, 0, NULL, 0); + assert_equals(info.exp_recv_size, recv_size); + assert_equals(info.exp_recv_data, buff); + bool ret = client->close(client); + assert_true(ret); + + KcSocket_delete(client); + + server->join(server); + KcThread_delete(server); +} + +/** + * TCP 送受信 + */ +static void test_socket_stream1(void) +{ + // 受信側 + KcThread *server = KcThread_new(test_socket_thread_echo_server); + TestSocketInfo info = { + .type = SOCK_STREAM, + .family = AF_UNSPEC, + .addr = "127.0.0.1", + .service = "5000", + .mcast_addr = NULL, + .mcast_iface = NULL, + .exp_recv_size = 4, + .exp_recv_data = "XYZ", + .exp_src_addr = NULL, + .exp_src_service = NULL}; + server->start(server, &info); + + KcThread_msleep(100, false); + + // 送信側 + KcSocket *client = KcSocket_new(SOCK_STREAM, AF_UNSPEC); + assert_not_null(client); + + bool ret = client->connect(client, info.addr, info.service, NULL, NULL); + assert_true(ret); + + // ソケット取得 + socket_t sock = client->get_socket(client); + assert_true(sock != INVALID_SOCKET); + + // タイプ確認 + int type = client->get_type(client); + assert_equals(SOCK_STREAM, type); + + // ファミリー確認 (IPv4 [AF_INET]) + int family = client->get_family(client); + assert_equals(AF_INET, family); + + int send_size = client->send(client, info.exp_recv_data, info.exp_recv_size, 0); + assert_equals(4, send_size); + + // 応答受信 + char buff[16]; + int recv_size = client->recv(client, buff, sizeof(buff), 0); + assert_equals(info.exp_recv_size, recv_size); + ret = client->close(client); + assert_true(ret); + + KcSocket_delete(client); + + server->join(server); + KcThread_delete(server); +} + +/** + * TCP 送受信 + */ +static void test_socket_stream2(void) +{ + // 受信側 + KcThread *server = KcThread_new(test_socket_thread_echo_server); + TestSocketInfo info = { + .type = SOCK_STREAM, + .family = AF_UNSPEC, + .addr = "127.0.0.1", + .service = "5000", + .mcast_addr = NULL, + .mcast_iface = NULL, + .exp_recv_size = 4, + .exp_recv_data = "XYZ", + .exp_src_addr = "127.0.0.1", + .exp_src_service = "6000"}; + server->start(server, &info); + + KcThread_msleep(100, false); + + // 送信側 + KcSocket *client = KcSocket_new(SOCK_STREAM, AF_UNSPEC); + assert_not_null(client); + + bool ret = client->connect(client, info.addr, info.service, info.exp_src_addr, info.exp_src_service); + assert_true(ret); + + // ソケット取得 + socket_t sock = client->get_socket(client); + assert_true(sock != INVALID_SOCKET); + + // タイプ確認 + int type = client->get_type(client); + assert_equals(SOCK_STREAM, type); + + // ファミリー確認 (IPv4 [AF_INET]) + int family = client->get_family(client); + assert_equals(AF_INET, family); + + const char *local_addr = client->get_local_addr(client); + assert_equals(info.exp_src_addr, local_addr); + int port = client->get_local_port(client); + assert_equals(atoi(info.exp_src_service), port); + + char buff[1024]; + client->print_info(client, buff, sizeof(buff)); + assert_equals("local : 127.0.0.1:6000\nremote: 127.0.0.1:5000\n", buff); + + int send_size = client->send(client, info.exp_recv_data, info.exp_recv_size, 0); + assert_equals(4, send_size); + + // 応答受信 + int recv_size = client->recv(client, buff, sizeof(buff), 0); + assert_equals(info.exp_recv_size, recv_size); + printf("buff=%s\n", buff); + ret = client->close(client); + assert_true(ret); + + KcSocket_delete(client); + + server->join(server); + KcThread_delete(server); +} + +/** + * マルチキャスト送受信 + */ +static void test_socket_mcast1_ipv4(void) +{ + // 受信側 + KcThread *server = KcThread_new(test_socket_thread_echo_server); + TestSocketInfo info = { + .type = SOCK_DGRAM, + .family = AF_UNSPEC, + .addr = "0.0.0.0", + .service = "5000", + .mcast_addr = "224.0.0.10", + .mcast_iface = NULL, + .exp_recv_size = 6, + .exp_recv_data = "AAAAA", + .exp_src_addr = NULL, + .exp_src_service = NULL}; + server->start(server, &info); + + KcThread_msleep(100, false); + + // 送信側 + KcSocket *client = KcSocket_new(SOCK_DGRAM, AF_UNSPEC); + assert_not_null(client); + + client->set_ifname(client, "10.0.2.15", NULL); + int send_size = client->sendto(client, info.exp_recv_data, info.exp_recv_size, 0, info.mcast_addr, info.service); + assert_equals(info.exp_recv_size, send_size); + + // ソケット取得 + socket_t sock = client->get_socket(client); + assert_true(sock != INVALID_SOCKET); + + // タイプ確認 + int type = client->get_type(client); + assert_equals(SOCK_DGRAM, type); + + // ファミリー確認 (IPv4 [AF_INET]) + int family = client->get_family(client); + assert_equals(AF_INET, family); + + // 応答受信 + char buff[16]; + int recv_size = client->recvfrom(client, buff, sizeof(buff), 0, NULL, 0, NULL, 0); + assert_equals(info.exp_recv_size, recv_size); + assert_equals((char *)info.exp_recv_data, buff); + bool ret = client->close(client); + assert_true(ret); + + KcSocket_delete(client); + + server->join(server); + KcThread_delete(server); } diff --git a/modules/test/src/ut.c b/modules/test/src/ut.c index 2a4e0d2..f91e83a 100644 --- a/modules/test/src/ut.c +++ b/modules/test/src/ut.c @@ -9,21 +9,21 @@ { // UT Setup - suite_assert(); - suite_dl(); - suite_env(); - suite_list_array(); - suite_list_linked(); - suite_lock_guard(); - suite_map(); - suite_memory_dump(); - suite_memory_entry(); - suite_memory_listener(); - suite_memory_mark(); - suite_memory(); - suite_queue(); - // suite_socket(); - suite_thread(); + // suite_assert(); + // suite_dl(); + // suite_env(); + // suite_list_array(); + // suite_list_linked(); + // suite_lock_guard(); + // suite_map(); + // suite_memory_dump(); + // suite_memory_entry(); + // suite_memory_listener(); + // suite_memory_mark(); + // suite_memory(); + // suite_queue(); + suite_socket(); + // suite_thread(); KcMemory_start(false); KcMemory_start(true);