- /**
- * @file kc_socket.c
- * @brief ソケットモジュール
- * @copyright 2003 - 2024 Nomura Kei
- */
- #include <stdio.h>
-
- #include <string.h>
- #include <errno.h>
-
- #include <kc.h>
- #include <kc_lock_guard.h>
- #include <kc_memory.h>
- #include <kc_socket.h>
-
- #if (KC_IS_WINDOWS)
- // Windows の setsockopt に指定する値は、様々な型のデータを渡すにもかかわらず、
- // const char* 型と定義されているため、明示的にキャストする。
- #define setsockopt(sockfd, level, optname, optval, optlen) \
- setsockopt(sockfd, level, optname, (const char *)optval, optlen)
- #endif // KC_IS_WINDOWS
-
- /**
- * KcSocket 管理情報
- */
- typedef struct
- {
- socket_t sock_fd; //!< ソケットディスクリプタ
- int sock_type; //!< ソケットタイプ
- int sock_family; //!< ソケットファミリ
- struct sockaddr_storage remote_addr; //!< リモートアドレス
- struct sockaddr_storage local_addr; //!< ローカルアドレス
- socklen_t remote_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;
-
- /**
- * アドレス情報
- */
- typedef struct
- {
- const char *addr; //!< アドレス
- const char *service; //!< サービス
- } KcSocketAddress;
-
- // =============================================================================
- // プロトタイプ宣言
- // =============================================================================
- static void Socket_setup(void);
- static void Socket_cleanup(void);
- 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);
- 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_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_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 bool KcSocket_set_ttl(KcSocket *sock, int val);
- 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(socket_t tmp_sock, int family, const char* mcast_v4_ifname, const char* mcast_v6_ifname);
- static int KcSocket_set_mcastif_ipv4(socket_t tmp_sock, const char *ifname);
- static int KcSocket_set_mcastif_ipv6(socket_t tmp_sock, const char *ifname);
- static int KcSocket_print_addr(char *buff, size_t size, const struct sockaddr *addr, size_t addrlen);
-
- /**
- * ソケットのセットアップをします。
- */
- static void Socket_setup(void)
- {
- static bool is_init = false;
- if (!is_init)
- {
- #if (KC_IS_WINDOWS)
- WSADATA wsa_data;
- WSAStartup(MAKEWORD(2, 0), &wsa_data);
- #endif
- atexit(Socket_cleanup);
- is_init = true;
- }
- }
-
- /**
- * ソケットライブラリのクリーンアップをします。
- */
- static void Socket_cleanup(void)
- {
- #if (KC_IS_WINDOWS)
- WSACleanup();
- #endif
- }
-
- // =============================================================================
- // new
- // =============================================================================
- /**
- * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。
- *
- * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW)
- * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6)
- */
- KcSocket *KcSocket_new(int type, int family)
- {
- // KcSocket の管理構造
- // +----------------+
- // | KcSocket |
- // | ... |
- // | _info -----------+
- // +----------------+ |
- // | <KcSocketInfo> |<--+
- // +----------------+
- Socket_setup();
- KcSocket *sock = (KcSocket *)malloc(sizeof(KcSocket) + sizeof(KcSocketInfo));
- if (sock != NULL)
- {
- sock->get_remote_addr = KcSocket_get_remote_addr;
- sock->get_remote_port = KcSocket_get_remote_port;
- sock->get_local_addr = KcSocket_get_local_addr;
- sock->get_local_port = KcSocket_get_local_port;
- sock->get_socket = KcSocket_get_socket;
- sock->get_type = KcSocket_get_type;
- sock->get_family = KcSocket_get_family;
- sock->get_addrinfo = KcSocket_get_addrinfo; // for local
- sock->bind = KcSocket_bind;
- sock->listen = KcSocket_listen;
- sock->accept = KcSocket_accept;
- sock->connect = KcSocket_connect;
- sock->close = KcSocket_close;
- sock->send = KcSocket_send;
- sock->recv = KcSocket_recv;
- sock->sendto = KcSocket_sendto;
- sock->recvfrom = KcSocket_recvfrom;
- 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;
- info->sock_type = type;
- 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;
- }
-
- // =============================================================================
- // delete
- // =============================================================================
- /**
- * Socket を破棄します。
- */
- void KcSocket_delete(KcSocket *socket)
- {
- if (socket)
- {
- if (((KcSocketInfo *)socket->_info)->sock_fd != INVALID_SOCKET)
- {
- socket->close(socket);
- }
- free(socket);
- }
- }
-
- // =============================================================================
- // get_remote_addr
- // =============================================================================
- /**
- * ソケットの接続先アドレスを返します。
- * アドレスが取得できない場合(未接続状態など)、NULL を返します。
- *
- * @param sock 対象ソケット
- * @return ソケットの接続先アドレス
- */
- static const char *KcSocket_get_remote_addr(KcSocket *sock)
- {
- KcSocketInfo *info = (KcSocketInfo *)sock->_info;
- if (info->remote_addrlen)
- {
- int ret = getnameinfo(
- (const struct sockaddr *)&info->remote_addr,
- info->remote_addrlen,
- info->tmp_remote_host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
- if (ret == 0)
- {
- return info->tmp_remote_host;
- }
- }
- return NULL;
- }
-
- /**
- * ソケットの接続先ポート番号を返します。
- * 接続先ポート番号が取得できなかった場合、-1 を返します。
- *
- * @param sock 対象ソケット
- * @return ポート番号
- */
- static int KcSocket_get_remote_port(KcSocket *sock)
- {
- KcSocketInfo *info = (KcSocketInfo *)sock->_info;
- if (info->remote_addrlen)
- {
- char tmp_port[NI_MAXSERV];
- int ret = getnameinfo(
- (const struct sockaddr *)&info->remote_addr,
- info->remote_addrlen,
- NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV);
- if (ret == 0)
- {
- return atoi(tmp_port);
- }
- }
- return -1;
- }
-
- /**
- * ソケットのローカルアドレスを返します。
- * アドレスが取得できない場合、NULL を返します。
- *
- * @param sock 対象ソケット
- * @return ソケットのローカルアドレス
- */
- 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->local_addrlen,
- info->tmp_local_host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
- if (ret == 0)
- {
- return info->tmp_local_host;
- }
- }
- return NULL;
- }
-
- /**
- * ソケットのローカルポート番号を返します。
- * ローカルポート番号が取得できなかった場合、-1 を返します。
- *
- * @param sock 対象ソケット
- * @return ポート番号
- */
- static int KcSocket_get_local_port(KcSocket *sock)
- {
- KcSocketInfo *info = (KcSocketInfo *)sock->_info;
- if (info->local_addrlen)
- {
- char tmp_port[NI_MAXSERV];
- int ret = getnameinfo(
- (const struct sockaddr *)&info->local_addr,
- info->local_addrlen,
- NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV);
- if (ret == 0)
- {
- return atoi(tmp_port);
- }
- }
- return -1;
- }
-
- /**
- * ソケットディスクリプタを返します。
- * 通常は、ソケットディスクリプタを直接操作しないでください。
- *
- * @param sock 対象ソケット
- * @return ソケットディスクリプタ
- */
- static socket_t KcSocket_get_socket(KcSocket *sock)
- {
- KcSocketInfo *info = (KcSocketInfo *)sock->_info;
- return info->sock_fd;
- }
-
- /**
- * ソケットタイプを返します。
- *
- * @param sock 対象ソケット
- * @return ソケットタイプ
- */
- static int KcSocket_get_type(KcSocket *sock)
- {
- KcSocketInfo *info = (KcSocketInfo *)sock->_info;
- return info->sock_type;
- }
-
- /**
- * ソケットファミリを返します。
- * ソケット接続、バインド時には、実際にバインド、
- * あるいは接続されているソケットファミリを返します。
- *
- * @param sock 対象ソケット
- * @return ソケットファミリ
- */
- static int KcSocket_get_family(KcSocket *sock)
- {
- KcSocketInfo *info = (KcSocketInfo *)sock->_info;
- int result_family = info->sock_family;
- if (info->sock_fd != INVALID_SOCKET)
- {
- struct sockaddr_storage ss;
- socklen_t len = sizeof(struct sockaddr_storage);
- if (getsockname(info->sock_fd, (struct sockaddr *)&ss, &len) == 0)
- {
- result_family = ss.ss_family;
- }
- }
- return result_family;
- }
-
- /**
- * 指定されたアドレス、サービスのアドレス情報を取得します。
- *
- * @param sock 対象ソケット
- * @param result アドレス情報格納用ポインタ
- * @param addr アドレス
- * @param service サービス
- * @param is_passive サーバソケットの場合、true を指定ください。
- */
- static bool KcSocket_get_addrinfo(
- KcSocket *sock, struct addrinfo **result,
- const char *addr, const char *service, bool is_passive)
- {
- KcSocketInfo *info = (KcSocketInfo *)sock->_info;
-
- struct addrinfo hints;
- memset(&hints, 0x00, sizeof(struct addrinfo));
- hints.ai_socktype = info->sock_type;
- hints.ai_family = info->sock_family;
- if (is_passive)
- {
- hints.ai_flags = AI_PASSIVE;
- }
- int ret = getaddrinfo(addr, service, &hints, result);
- return (ret == 0);
- }
-
- /**
- * 指定されたアドレス、サービスにバインドします。
- * 指定されたアドレスとサービスにより、複数候補がある場合は、
- * 最初に見つかったアドレスとポートにバインドします。
- *
- * @param sock 対象ソケット
- * @param addr アドレス
- * @param service サービス (例: "80", "http", "ssh" など)
- * @return true/false (bind 成功/失敗)
- */
- static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service)
- {
- struct addrinfo *bind_addr;
- bool is_success = sock->get_addrinfo(sock, &bind_addr, addr, service, true);
- if (is_success)
- {
- is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, NULL);
- freeaddrinfo(bind_addr);
- }
- return is_success;
- }
-
- /**
- * ソケットを接続待ちソケットとしてマークをつけます。
- * 保留中の接続のキュー最大長を指定します。
- *
- * @param sock 対象ソケット
- * @param backlog バックログ
- * @return true/false (成功/失敗)
- */
- static bool KcSocket_listen(KcSocket *sock, int backlog)
- {
- bool result = false;
- KcSocketInfo *info = (KcSocketInfo *)sock->_info;
- if (info->sock_fd != INVALID_SOCKET)
- {
- int ret = listen(info->sock_fd, backlog);
- result = (ret != SOCKET_ERROR);
- }
- return result;
- }
-
- /**
- * ソケットへの接続を受け付けます。
- *
- * @param sock 対象ソケット
- * @return 受け付けたソケット(失敗時NULL)
- */
- static KcSocket *KcSocket_accept(KcSocket *sock)
- {
- KcSocketInfo *info = (KcSocketInfo *)sock->_info;
- KcSocket *client = NULL;
- if (info->sock_fd != INVALID_SOCKET)
- {
- client = KcSocket_new(info->sock_type, info->sock_family);
- if (client != NULL)
- {
- KcSocketInfo *cinfo = (KcSocketInfo *)client->_info;
- cinfo->remote_addrlen = sizeof(struct sockaddr_storage);
- cinfo->sock_fd = accept(info->sock_fd, (struct sockaddr *)&cinfo->remote_addr, &cinfo->remote_addrlen);
- if (cinfo->sock_fd != INVALID_SOCKET)
- {
- memcpy(&cinfo->local_addr, &info->local_addr, info->local_addrlen);
- cinfo->local_addrlen = info->local_addrlen;
- cinfo->sock_type = SOCK_STREAM;
- cinfo->sock_family = cinfo->remote_addr.ss_family;
- }
- else
- {
- KcSocket_delete(client);
- client = NULL;
- }
- }
- }
- return client;
- }
-
- /**
- * 指定されたアドレス、サービス接続します。
- * local_addr に NULL を指定した場合、自動的にローカルのアドレス、サービスが設定されます。
- *
- * @param sock 対象ソケット
- * @param addr アドレス
- * @param service サービス
- * @param local_addr ローカルアドレス
- * @param local_service ローカルサービス
- * @return true/false (成功/失敗)
- */
- static bool KcSocket_connect(
- KcSocket *sock, const char *addr, const char *service,
- const char *local_addr, const char *local_service)
- {
- // 接続先アドレス情報取得
- struct addrinfo *conn_addr = NULL;
- if (!sock->get_addrinfo(sock, &conn_addr, addr, service, false))
- {
- return false;
- }
-
- // バインドアドレス情報取得
- bool is_success = false;
- struct addrinfo *bind_addr = NULL;
- if (local_addr != NULL)
- { // bind が必要
- is_success = sock->get_addrinfo(sock, &bind_addr, local_addr, local_service, true);
- if (is_success)
- {
- is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, conn_addr);
- freeaddrinfo(bind_addr);
- }
- }
- else
- {
- is_success = KcSocket_addrinfo_connect(sock, conn_addr, INVALID_SOCKET);
- }
- freeaddrinfo(conn_addr);
- return is_success;
- }
-
- /**
- * ソケットをクローズします。
- *
- * @param sock 対象ソケット
- * @return true/false (成功/失敗)
- */
- static bool KcSocket_close(KcSocket *sock)
- {
- KcSocketInfo *info = (KcSocketInfo *)sock->_info;
- if (info->sock_type == SOCK_STREAM)
- { // TCP の場合は、出力を閉じて、届いているデータを読み取ってから close する。
- int ret = shutdown(info->sock_fd, SHUT_WR);
- if (ret == 0)
- {
- int read_size;
- char buff[1024];
- do
- { // 届いているデータを全て読み取る
- read_size = recv(info->sock_fd, buff, sizeof(buff), 0);
- } while (read_size > 0);
- }
- }
- sockclose(info->sock_fd);
- return true;
- }
-
- /**
- * メッセージを送信します。
- *
- * @param sock 対象ソケット
- * @param buff メッセージ
- * @param size サイズ
- * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB)
- * @return 送信されたデータサイズ
- */
- static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags)
- {
- KcSocketInfo *info = (KcSocketInfo *)sock->_info;
- ssize_t write_size = 0;
- do
- {
- errno = 0;
- write_size = send(info->sock_fd, buff, size, flags);
- } while ((write_size < 0) && (errno == EINTR));
- return write_size;
- }
-
- /**
- * メッセージを受信します。
- * 受信したメッセージは指定されたバッファに格納されます。
- *
- * @param sock 対象ソケット
- * @param buff メッセージ受信用バッファ
- * @param size バッファサイズ
- * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB)
- * @return 読み取ったデータサイズ
- */
- static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags)
- {
- KcSocketInfo *info = (KcSocketInfo *)sock->_info;
- ssize_t read_size;
- do
- {
- errno = 0;
- read_size = recv(info->sock_fd, buff, size, flags);
- } while ((read_size < 0) && (errno == EINTR));
- return read_size;
- }
-
- /**
- * 指定されたアドレス、サービスにメッセージを送信します。
- *
- * @param sock 対象ソケット
- * @param buff メッセージ
- * @param size サイズ
- * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB)
- * @param addr アドレス
- * @param service サービス
- * @return 送信メッセージサイズ、エラー発生時は -1
- */
- static ssize_t KcSocket_sendto(
- KcSocket *sock, const char *buff, size_t size, int flags,
- const char *addr, const char *service)
- {
- // 接続先アドレス情報取得
- 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(tmp_sock, rp->ai_family, info->mcast_v4_ifname, info->mcast_v6_ifname);
-
- 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;
- }
-
- /**
- * メッセージを受信します。
- * src_addr, src_service がいずれも NULL 出ない場合、
- * 送信元のアドレスとサービスが格納されます。
- *
- * @param sock 対象ソケット
- * @param buff メッセージ受信用バッファ
- * @param size バッファサイズ
- * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB)
- * @param src_addr 送信元アドレス格納用バッファ
- * @param src_addrlen 送信元アドレス格納用バッファサイズ
- * @param src_service 送信元サービス格納用バッファ
- * @param src_servicelen 送信元サービス格納用バッファサイズ
- * @return 受信メッセージサイズ、エラー発生時は -1
- */
- 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)
- {
- 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;
- }
-
- /**
- * TTL を設定します。
- *
- * @param sock 対象ソケット
- * @param val 設定する TTL
- * @return true/false (成功/失敗)
- */
- static bool KcSocket_set_ttl(KcSocket *sock, int val)
- {
- 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 インタフェース名
- * @return true/false (成功/失敗)
- */
- static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname)
- {
- 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);
- }
-
- /**
- * マルチキャストグループから離脱します。
- *
- * @param sock 対象ソケット
- * @param addr マルチキャストアドレス
- * @param ifname インタフェース名
- * @return true/false (成功/失敗)
- */
- static bool KcSocket_leave(KcSocket *sock, const char *addr, const char *ifname)
- {
- 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;
- printf("v4 %s\n", info->mcast_v4_ifname);
- printf("v6 %s\n", info->mcast_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);
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- //
- // [内部関数]
- //
-
- /**
- * 指定された接続アドレス情報をもとにソケットの生成、接続を試みます。
- * 接続済みソケット sockfd が有効な場合、sockfd を用いて接続します。
- *
- * @param sock 対象ソケット
- * @param conn_addrinfo 接続アドレス情報
- * @param sockfd 接続済みソケット
- * @return true/false (接続成功/失敗)
- */
- static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd)
- {
- bool is_success = false;
- socket_t tmp_sock = sockfd;
- for (struct addrinfo *rp = conn_addrinfo; rp != NULL; rp = rp->ai_next)
- {
- if (sockfd == INVALID_SOCKET)
- { // sockfd が無効の場合、ソケットを生成する。
- tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
- if (tmp_sock == INVALID_SOCKET)
- {
- continue;
- }
- }
-
- int ret = connect(tmp_sock, rp->ai_addr, rp->ai_addrlen);
- is_success = (ret != SOCKET_ERROR);
- if (is_success)
- {
- 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)
- { // sockfd が無効の場合、一時的に生成したソケットをクローズする。
- sockclose(tmp_sock);
- }
- }
- return is_success;
- }
-
- /**
- * 指定された接続アドレス情報をもとにソケットの生成、バインドを試みます。
- * conn_addrinfo が NULL でない場合、バインド後に接続を試みます。
- *
- * @param sock 対象ソケット
- * @param bind_addrinfo バインドアドレス情報
- * @param conn_addrinfo 接続アドレス情報
- * @return true/false (接続成功/失敗)
- */
- static bool KcSocket_addrinfo_bind_and_connect(
- KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo)
- {
- int ret;
- bool is_success = false;
-
- for (struct addrinfo *rp = bind_addrinfo; rp != NULL; rp = rp->ai_next)
- { // sokcet : ソケット生成
- socket_t tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
- if (tmp_sock == INVALID_SOCKET)
- {
- continue;
- }
-
- // bind
- 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 == 0);
- if (!is_success)
- { // bind 失敗したので次へ
- sockclose(tmp_sock);
- continue;
- }
-
- // connect
- if (conn_addrinfo)
- {
- is_success = KcSocket_addrinfo_connect(sock, conn_addrinfo, tmp_sock);
- if (!is_success)
- { // connect 失敗したので次へ
- sockclose(tmp_sock);
- continue;
- }
- }
-
- // 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(
- socket_t tmp_sock, int family,
- const char* mcast_v4_ifname, const char* mcast_v6_ifname)
- {
- int ret = SOCKET_ERROR;
- switch (family)
- {
- case AF_INET:
- ret = (mcast_v4_ifname)
- ? KcSocket_set_mcastif_ipv4(tmp_sock, mcast_v4_ifname)
- : 0;
- break;
- case AF_INET6:
- ret = (mcast_v6_ifname)
- ? KcSocket_set_mcastif_ipv6(tmp_sock, 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(socket_t tmp_sock, const char *ifname)
- {
- struct in_addr ifaddr;
- ifaddr.s_addr = (ifname != NULL) ? inet_addr(ifname) : htonl(INADDR_ANY);
- int ret = setsockopt(tmp_sock, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr, sizeof(ifaddr));
- return ret;
- }
-
- /**
- * マルチキャストを送信するためのインタフェースを設定します(IPv6)。
- *
- * @param sock 対象一時ソケット
- * @param ifname インタフェース名
- * @return 0/-1 (成功/失敗)
- */
- static int KcSocket_set_mcastif_ipv6(socket_t tmp_sock, const char *ifname)
- {
- unsigned int ifindex = (ifname != NULL) ? if_nametoindex(ifname) : 0;
- int ret = setsockopt(tmp_sock, 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;
- }