| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2000-2026 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / common tools sub-project | |
| * | |
| * GPAC is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU Lesser General Public License as published by | |
| * the Free Software Foundation; either version 2, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include <gpac/network.h> | |
| #ifndef GPAC_DISABLE_NETWORK | |
| #include <gpac/utf.h> | |
| #if defined(WIN32) || defined(_WIN32_WCE) | |
| #define _WINSOCK_DEPRECATED_NO_WARNINGS | |
| #ifdef _WIN32_WCE | |
| #include <winsock.h> | |
| #if !defined(__GNUC__) | |
| #pragma comment(lib, "winsock") | |
| #endif | |
| #else | |
| #include <sys/timeb.h> | |
| #include <winsock2.h> | |
| #include <ws2tcpip.h> | |
| #if !defined(__GNUC__) | |
| #pragma comment(lib, "ws2_32") | |
| #pragma comment(lib, "IPHLPAPI.lib") | |
| #define GPAC_WIN_HAS_ADAPTER_INFO | |
| #endif | |
| #if !defined(POLLIN) | |
| #ifdef GPAC_HAS_POLL | |
| #undef GPAC_HAS_POLL | |
| #endif | |
| #endif | |
| #ifdef GPAC_HAS_POLL | |
| #define GF_POLLFD WSAPOLLFD | |
| #define poll WSAPoll | |
| #endif | |
| #endif | |
| #include <windows.h> | |
| #if defined(IPV6_MULTICAST_IF) | |
| #define GPAC_HAS_IPV6 1 | |
| #else | |
| #undef GPAC_HAS_IPV6 | |
| #endif | |
| /* | |
| #if !defined(__GNUC__) | |
| #if defined(GPAC_HAS_IPV6) | |
| #pragma message("Using WinSock IPV6") | |
| #else | |
| #pragma message("Using WinSock IPV4") | |
| #endif | |
| #endif | |
| */ | |
| #include <errno.h> | |
| #ifndef EISCONN | |
| /*common win32 redefs*/ | |
| #undef EAGAIN | |
| #define EAGAIN WSAEWOULDBLOCK | |
| #define EISCONN WSAEISCONN | |
| #define ENOTCONN WSAENOTCONN | |
| #define ECONNRESET WSAECONNRESET | |
| #define EINPROGRESS WSAEINPROGRESS | |
| #define EALREADY WSAEALREADY | |
| #define EMSGSIZE WSAEMSGSIZE | |
| #define ECONNABORTED WSAECONNABORTED | |
| #define ENETDOWN WSAENETDOWN | |
| #undef EINTR | |
| #define EINTR WSAEINTR | |
| #undef EBADF | |
| #define EBADF WSAEBADF | |
| #endif | |
| #define LASTSOCKERROR WSAGetLastError() | |
| /*the number of sockets used. This because the WinSock lib needs init*/ | |
| static int wsa_init = 0; | |
| /*end-win32*/ | |
| #define SOCKET_INVALID(_s) (_s==INVALID_SOCKET) | |
| #else | |
| /*non-win32*/ | |
| #include <unistd.h> | |
| #include <fcntl.h> | |
| #include <netdb.h> | |
| #ifndef __BEOS__ | |
| #include <errno.h> | |
| #endif | |
| #ifndef __DARWIN__ | |
| #include <sys/time.h> | |
| #endif | |
| #include <netinet/in.h> | |
| #include <netinet/tcp.h> | |
| #include <sys/socket.h> | |
| #include <sys/types.h> | |
| #include <arpa/inet.h> | |
| #if defined(__FreeBSD__) | |
| #include <sys/stat.h> | |
| #endif | |
| /*not defined on solaris*/ | |
| #if !defined(INADDR_NONE) | |
| # if (defined(sun) && defined(__SVR4)) | |
| # define INADDR_NONE -1 | |
| # else | |
| # define INADDR_NONE ((unsigned long)-1) | |
| # endif | |
| #endif | |
| #define SOCKET_INVALID(_s) (_s<0) | |
| #define INVALID_SOCKET -1 | |
| #define SOCKET_ERROR -1 | |
| #define LASTSOCKERROR errno | |
| typedef s32 SOCKET; | |
| #define closesocket(v) close(v) | |
| #ifdef GPAC_HAS_POLL | |
| #include <poll.h> | |
| #define GF_POLLFD struct pollfd | |
| #endif | |
| #endif /*WIN32||_WIN32_WCE*/ | |
| #ifdef GPAC_BUILD_FOR_WINXP | |
| /* Added by M. Lackner to ensure Windows XP & XP x64 compatibility */ | |
| /* Adding PlibC's inet_ntop() function in a very slightly modified | |
| and renamed variant for IPv6 address conversion support on Win XP's, | |
| which is called by gf_sk_get_remote_address() further below. | |
| The renamed function is called inet_ntop_xp(). | |
| Original source code taken from here: | |
| https://sourceforge.net/projects/plibc/ | |
| This inet_ntop() implementation is (C) 1996-2001 Internet Software | |
| Consortium and is licensed under the ISC license. | |
| License text: https://opensource.org/licenses/ISC */ | |
| #define NS_INT16SZ 2 | |
| #define NS_IN6ADDRSZ 16 | |
| /* | |
| * WARNING: Don't even consider trying to compile this on a system where | |
| * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. | |
| */ | |
| static const char *inet_ntop4(const unsigned char *src, char *dst, size_t size); | |
| #ifdef AF_INET6 | |
| static const char *inet_ntop6(const unsigned char *src, char *dst, size_t size); | |
| #endif | |
| /* char * | |
| * isc_net_ntop(af, src, dst, size) | |
| * convert a network format address to presentation format. | |
| * return: | |
| * pointer to presentation format address (`dst'), or NULL (see errno). | |
| * author: | |
| * Paul Vixie, 1996. | |
| */ | |
| const char * inet_ntop_xp(int af, const void *src, char *dst, size_t size) | |
| { | |
| switch (af) { | |
| case AF_INET: | |
| return (inet_ntop4(src, dst, size)); | |
| #ifdef AF_INET6 | |
| case AF_INET6: | |
| return (inet_ntop6(src, dst, size)); | |
| #endif | |
| default: | |
| errno = EAFNOSUPPORT; | |
| return (NULL); | |
| } | |
| /* NOTREACHED */ | |
| } | |
| /* const char * | |
| * inet_ntop4(src, dst, size) | |
| * format an IPv4 address | |
| * return: | |
| * `dst' (as a const) | |
| * notes: | |
| * (1) uses no statics | |
| * (2) takes a unsigned char* not an in_addr as input | |
| * author: | |
| * Paul Vixie, 1996. | |
| */ | |
| static const char * inet_ntop4(const unsigned char *src, char *dst, size_t size) | |
| { | |
| static const char *fmt = "%u.%u.%u.%u"; | |
| char tmp[sizeof "255.255.255.255"]; | |
| size_t len; | |
| len = snprintf(tmp, sizeof tmp, fmt, src[0], src[1], src[2], src[3]); | |
| if (len >= size) { | |
| errno = ENOSPC; | |
| return (NULL); | |
| } | |
| memcpy(dst, tmp, len + 1); | |
| return (dst); | |
| } | |
| /* const char * | |
| * isc_inet_ntop6(src, dst, size) | |
| * convert IPv6 binary address into presentation (printable) format | |
| * author: | |
| * Paul Vixie, 1996. | |
| */ | |
| #ifdef AF_INET6 | |
| static const char * inet_ntop6(const unsigned char *src, char *dst, size_t size) | |
| { | |
| /* | |
| * Note that int32_t and int16_t need only be "at least" large enough | |
| * to contain a value of the specified size. On some systems, like | |
| * Crays, there is no such thing as an integer variable with 16 bits. | |
| * Keep this in mind if you think this function should have been coded | |
| * to use pointer overlays. All the world's not a VAX. | |
| */ | |
| char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; | |
| struct { int base, len; } best, cur; | |
| unsigned int words[NS_IN6ADDRSZ / NS_INT16SZ]; | |
| int i, inc; | |
| /* | |
| * Preprocess: | |
| * Copy the input (bytewise) array into a wordwise array. | |
| * Find the longest run of 0x00's in src[] for :: shorthanding. | |
| */ | |
| memset(words, '\0', sizeof words); | |
| for (i = 0; i < NS_IN6ADDRSZ; i++) | |
| words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); | |
| best.base = -1; | |
| best.len = 0; | |
| cur.base = -1; | |
| cur.len = 0; | |
| for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { | |
| if (words[i] == 0) { | |
| if (cur.base == -1) | |
| cur.base = i, cur.len = 1; | |
| else | |
| cur.len++; | |
| } | |
| else { | |
| if (cur.base != -1) { | |
| if (best.base == -1 || cur.len > best.len) | |
| best = cur; | |
| cur.base = -1; | |
| } | |
| } | |
| } | |
| if (cur.base != -1) { | |
| if (best.base == -1 || cur.len > best.len) | |
| best = cur; | |
| } | |
| if (best.base != -1 && best.len < 2) | |
| best.base = -1; | |
| /* | |
| * Format the result. | |
| */ | |
| tp = tmp; | |
| for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { | |
| /* Are we inside the best run of 0x00's? */ | |
| if (best.base != -1 && i >= best.base && i < (best.base + best.len)) { | |
| if (i == best.base) | |
| *tp++ = ':'; | |
| continue; | |
| } | |
| /* Are we following an initial run of 0x00s or any real hex? */ | |
| if (i != 0) | |
| *tp++ = ':'; | |
| /* Is this address an encapsulated IPv4? */ | |
| if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { | |
| if (!inet_ntop4(src + 12, tp, sizeof tmp - (tp - tmp))) | |
| return (NULL); | |
| tp += strlen(tp); | |
| break; | |
| } | |
| inc = snprintf(tp, 5, "%x", words[i]); | |
| tp += inc; | |
| } | |
| /* Was it a trailing run of 0x00's? */ | |
| if (best.base != -1 && (best.base + best.len) == (NS_IN6ADDRSZ / NS_INT16SZ)) | |
| *tp++ = ':'; | |
| *tp++ = '\0'; | |
| /* | |
| * Check for overflow, copy, and we're done. | |
| */ | |
| if ((size_t)(tp - tmp) > size) { | |
| errno = ENOSPC; | |
| return (NULL); | |
| } | |
| memcpy(dst, tmp, tp - tmp); | |
| return (dst); | |
| } | |
| #endif /* AF_INET6 */ | |
| /* End of inet_ntop() reimplementation */ | |
| #endif //winxp | |
| #ifdef GPAC_BUILD_FOR_WINXP | |
| #define my_inet_ntop inet_ntop_xp | |
| #else | |
| #define my_inet_ntop inet_ntop | |
| #endif | |
| #ifdef GPAC_HAS_IPV6 | |
| # ifndef IPV6_ADD_MEMBERSHIP | |
| # define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP | |
| # endif | |
| # ifndef IPV6_DROP_MEMBERSHIP | |
| # define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP | |
| # endif | |
| #endif | |
| #ifdef __SYMBIAN32__ | |
| #define SSO_CAST | |
| #else | |
| #define SSO_CAST (const char *) | |
| #endif | |
| #ifdef GPAC_HAS_IPV6 | |
| static u32 ipv6_check_state = 0; | |
| #endif | |
| #ifdef GPAC_HAS_SOCK_UN | |
| #include <sys/un.h> | |
| #endif | |
| /*internal flags*/ | |
| enum | |
| { | |
| GF_SOCK_IS_TCP = 1<<9, | |
| GF_SOCK_IS_IPV6 = 1<<10, | |
| GF_SOCK_NON_BLOCKING = 1<<11, | |
| GF_SOCK_IS_MULTICAST = 1<<12, | |
| GF_SOCK_IS_LISTENING = 1<<13, | |
| /*socket is bound to a specific dest (server) or source (client) */ | |
| GF_SOCK_HAS_PEER = 1<<14, | |
| GF_SOCK_IS_UN = 1<<15, | |
| GF_SOCK_HAS_CONNECT = 1<<16 | |
| }; | |
| #ifndef GPAC_DISABLE_NETCAP | |
| typedef struct __netcap_filter GF_NetcapFilter; | |
| static GF_NetcapFilter *gf_net_filter_get(const char *id); | |
| typedef struct | |
| { | |
| //local host info | |
| u32 host_addr_v4; | |
| bin128 host_addr_v6; | |
| u32 host_port; | |
| //remote peer info | |
| u32 peer_addr_v4; | |
| bin128 peer_addr_v6; | |
| u32 peer_port; | |
| u32 pck_idx_r, pck_idx_w, next_pck_range, next_rand; | |
| u32 patch_offset, patch_val; | |
| GF_NetcapFilter *nf; | |
| } NetCapInfo; | |
| #endif | |
| struct __tag_socket | |
| { | |
| u32 flags; | |
| SOCKET socket; | |
| /*destination address for sendto/recvfrom*/ | |
| #ifdef GPAC_HAS_IPV6 | |
| struct sockaddr_storage dest_addr; | |
| #else | |
| struct sockaddr_in dest_addr; | |
| #endif | |
| u32 dest_addr_len; | |
| u32 usec_wait; | |
| #ifdef GPAC_HAS_POLL | |
| u32 poll_idx; | |
| #endif | |
| #ifndef GPAC_DISABLE_NETCAP | |
| NetCapInfo *cap_info; | |
| #endif | |
| }; | |
| GF_EXPORT | |
| u32 gf_net_has_ipv6() | |
| { | |
| #ifdef GPAC_HAS_IPV6 | |
| if (!ipv6_check_state) { | |
| SOCKET s; | |
| #ifdef WIN32 | |
| if (!wsa_init) { | |
| WSADATA Data; | |
| if (WSAStartup(0x0202, &Data)!=0) { | |
| ipv6_check_state = 1; | |
| return 0; | |
| } | |
| } | |
| #endif | |
| s = socket(PF_INET6, SOCK_STREAM, 0); | |
| if (!s) ipv6_check_state = 1; | |
| else { | |
| ipv6_check_state = 2; | |
| closesocket(s); | |
| } | |
| #ifdef WIN32 | |
| if (!wsa_init) WSACleanup(); | |
| #endif | |
| } | |
| return (ipv6_check_state==2); | |
| #else | |
| return 0; | |
| #endif | |
| } | |
| GF_EXPORT | |
| Bool gf_net_is_ipv6(const char *address) | |
| { | |
| u32 i, nb_seps=0, nb_dseps=0, nb_dig=0, count = address ? (u32) strlen(address) : 0; | |
| for (i=0;i<count; i++) { | |
| if (address[i]==':') { | |
| nb_seps++; | |
| nb_dig=0; | |
| if ((i+1<count) && (address[i+1]==':')) { | |
| nb_dseps++; | |
| i++; | |
| } | |
| } | |
| else if (strchr("0123456789ABCDEFabcdef",address[i])==NULL) { | |
| return GF_FALSE; | |
| } else { | |
| nb_dig++; | |
| if (nb_dig>4) return GF_FALSE; | |
| } | |
| } | |
| if (nb_seps>7) return GF_FALSE; | |
| if (nb_seps==7) return GF_TRUE; | |
| if (nb_dseps!=1) return GF_FALSE; | |
| return GF_TRUE; | |
| } | |
| #if defined(GPAC_WIN_HAS_ADAPTER_INFO) | |
| #include <iphlpapi.h> | |
| #endif | |
| #if defined(GPAC_HAS_IFADDRS) | |
| #include <ifaddrs.h> | |
| #include <net/if.h> | |
| #endif | |
| #ifdef GPAC_HAS_IPV6 | |
| #define MAX_PEER_NAME_LEN 1024 | |
| static struct addrinfo *gf_sk_get_ipv6_addr(const char *PeerName, u16 PortNumber, int family, int flags, int sock_type) | |
| { | |
| struct addrinfo *res=NULL; | |
| struct addrinfo hints; | |
| char node[MAX_PEER_NAME_LEN], portstring[20]; | |
| char *service, *dest; | |
| service = dest = NULL; | |
| #ifdef WIN32 | |
| if (!wsa_init) { | |
| WSADATA Data; | |
| if (WSAStartup(0x0202, &Data)!=0) return NULL; | |
| wsa_init = 1; | |
| } | |
| #endif | |
| service = dest = NULL; | |
| memset(&hints, 0, sizeof(hints)); | |
| hints.ai_socktype = sock_type; | |
| hints.ai_family = family; | |
| hints.ai_flags = flags; | |
| if (PortNumber) { | |
| sprintf (portstring, "%d", PortNumber); | |
| service = (char *)portstring; | |
| } | |
| if (PeerName) { | |
| gf_strlcpy(node, PeerName, MAX_PEER_NAME_LEN); | |
| if (node[0]=='[') { | |
| node[strlen(node)-1] = 0; | |
| memmove(node, &node[1], MAX_PEER_NAME_LEN-1); | |
| } | |
| node[MAX_PEER_NAME_LEN - 1] = 0; | |
| dest = (char *) node; | |
| } | |
| if (getaddrinfo((const char *)dest, (const char *)service, &hints, &res) != 0) return NULL; | |
| return res; | |
| } | |
| static struct addrinfo *gf_sk_get_ifce_ipv6_addr(const char *ifce_name_or_ip, u16 PortNumber, int family, int flags, int sock_type) | |
| { | |
| char szIP[100]; | |
| Bool force_ipv6 = GF_FALSE; | |
| if (!ifce_name_or_ip || !ifce_name_or_ip[0] || gf_net_is_ipv6(ifce_name_or_ip) || strchr(ifce_name_or_ip, '.')) | |
| return gf_sk_get_ipv6_addr(ifce_name_or_ip, PortNumber, family, flags, sock_type); | |
| if (ifce_name_or_ip[0]=='+') { | |
| force_ipv6 = GF_TRUE; | |
| ifce_name_or_ip+=1; | |
| if (!ifce_name_or_ip[0]) { | |
| return gf_sk_get_ipv6_addr(ifce_name_or_ip, PortNumber, AF_INET6, flags, sock_type); | |
| } | |
| } | |
| if (family==AF_INET6) force_ipv6 = GF_TRUE; | |
| szIP[0] = 0; | |
| #if defined(GPAC_WIN_HAS_ADAPTER_INFO) | |
| IP_ADAPTER_ADDRESSES *AdapterInfo = malloc(sizeof(IP_ADAPTER_ADDRESSES)); | |
| ULONG ulOutBufLen = sizeof(AdapterInfo); | |
| if (AdapterInfo && (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, AdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)) { | |
| free(AdapterInfo); | |
| AdapterInfo = malloc(sizeof(IP_ADAPTER_ADDRESSES)*ulOutBufLen); | |
| } | |
| if (AdapterInfo) | |
| GetAdaptersAddresses(AF_UNSPEC, 0, NULL, AdapterInfo, &ulOutBufLen); | |
| IP_ADAPTER_ADDRESSES *ifa = AdapterInfo; | |
| while (ifa) { | |
| Bool match = GF_FALSE; | |
| if (!strcmp(ifa->AdapterName, ifce_name_or_ip)) match = GF_TRUE; | |
| char *name = gf_wcs_to_utf8(ifa->FriendlyName); | |
| if (name) { | |
| if (!strcmp(name, ifce_name_or_ip)) match = GF_TRUE; | |
| gf_free(name); | |
| } | |
| if (!match) { | |
| ifa = ifa->Next; | |
| szIP[0] = 0; | |
| continue; | |
| } | |
| IP_ADAPTER_UNICAST_ADDRESS_LH *ipadd = ifa->FirstUnicastAddress; | |
| while (ipadd) { | |
| if (!ipadd->Address.lpSockaddr) { | |
| ipadd = ipadd->Next; | |
| continue; | |
| } | |
| if (ipadd->Address.lpSockaddr->sa_family==AF_INET) { | |
| my_inet_ntop(ipadd->Address.lpSockaddr->sa_family, ipadd->Address.lpSockaddr, szIP, 100); | |
| free(AdapterInfo); | |
| return gf_sk_get_ipv6_addr(szIP, PortNumber, family, flags, sock_type); | |
| } | |
| if (ipadd->Address.lpSockaddr->sa_family == AF_INET6) { | |
| my_inet_ntop(ipadd->Address.lpSockaddr->sa_family, ipadd->Address.lpSockaddr, szIP, 100); | |
| if (force_ipv6) { | |
| free(AdapterInfo); | |
| return gf_sk_get_ipv6_addr(szIP, PortNumber, family, flags, sock_type); | |
| } | |
| } | |
| ipadd = ipadd->Next; | |
| } | |
| ifa = ifa->Next; | |
| } | |
| if (AdapterInfo) | |
| free(AdapterInfo); | |
| #endif | |
| #if defined(GPAC_HAS_IFADDRS) | |
| struct ifaddrs *ifap = NULL, *ifa; | |
| getifaddrs(&ifap); | |
| ifa = ifap; | |
| while (ifa) { | |
| if (!ifa->ifa_addr || strcmp(ifa->ifa_name, ifce_name_or_ip)) { | |
| ifa = ifa->ifa_next; | |
| continue; | |
| } | |
| if (ifa->ifa_addr->sa_family==AF_INET) { | |
| memset(szIP, 0, 100); | |
| inet_ntop(AF_INET, &( ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr), szIP, 100); | |
| freeifaddrs(ifap); | |
| return gf_sk_get_ipv6_addr(szIP, PortNumber, family, flags, sock_type); | |
| } | |
| if (ifa->ifa_addr->sa_family==AF_INET6) { | |
| memset(szIP, 0, 100); | |
| inet_ntop(AF_INET6, &( ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr), szIP, 100); | |
| if (force_ipv6) { | |
| freeifaddrs(ifap); | |
| return gf_sk_get_ipv6_addr(szIP, PortNumber, family, flags, sock_type); | |
| } | |
| } | |
| ifa = ifa->ifa_next; | |
| } | |
| freeifaddrs(ifap); | |
| #endif | |
| if (szIP[0]) { | |
| return gf_sk_get_ipv6_addr(szIP, PortNumber, family, flags, sock_type); | |
| } | |
| return NULL; | |
| } | |
| #endif | |
| GF_EXPORT | |
| GF_Err gf_sk_get_host_name(char *buffer) | |
| { | |
| s32 ret = gethostname(buffer, GF_MAX_IP_NAME_LEN); | |
| return (ret == SOCKET_ERROR) ? GF_IP_ADDRESS_NOT_FOUND : GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sk_get_local_ip(GF_Socket *sock, char buffer[GF_MAX_IP_NAME_LEN]) | |
| { | |
| #ifdef GPAC_HAS_IPV6 | |
| char clienthost[NI_MAXHOST]; | |
| if (sock->flags & GF_SOCK_HAS_PEER) { | |
| if (getnameinfo((struct sockaddr *)&sock->dest_addr, sock->dest_addr_len, clienthost, sizeof(clienthost), NULL, 0, NI_NUMERICHOST)) | |
| return GF_IP_NETWORK_FAILURE; | |
| } else { | |
| struct sockaddr_storage clientaddr; | |
| socklen_t addrlen = sizeof(clientaddr); | |
| if (sock->flags & GF_SOCK_IS_MULTICAST) | |
| return GF_BAD_PARAM; | |
| if (getsockname(sock->socket, (struct sockaddr *)&clientaddr, &addrlen)) return GF_IP_NETWORK_FAILURE; | |
| if (getnameinfo((struct sockaddr *)&clientaddr, addrlen, clienthost, sizeof(clienthost), NULL, 0, NI_NUMERICHOST)) | |
| return GF_IP_NETWORK_FAILURE; | |
| } | |
| gf_strlcpy(buffer, clienthost, GF_MAX_IP_NAME_LEN); | |
| #else | |
| char *ip; | |
| if (sock->flags & GF_SOCK_HAS_PEER) { | |
| ip = inet_ntoa(sock->dest_addr.sin_addr); | |
| } else { | |
| struct sockaddr_in name; | |
| u32 len = sizeof(struct sockaddr_in); | |
| if (getsockname(sock->socket, (struct sockaddr*) &name, &len)) return GF_IP_NETWORK_FAILURE; | |
| ip = inet_ntoa(name.sin_addr); | |
| } | |
| if (!ip) return GF_IP_NETWORK_FAILURE; | |
| gf_strlcpy(buffer, ip, GF_MAX_IP_NAME_LEN); | |
| #endif | |
| return GF_OK; | |
| } | |
| #ifndef GPAC_DISABLE_NETCAP | |
| #include <gpac/bitstream.h> | |
| #include <gpac/list.h> | |
| enum | |
| { | |
| NETCAP_IS_MCAST = 1, | |
| NETCAP_IS_TCP = 1<<1, | |
| NETCAP_HAS_PEER = 1<<2, | |
| NETCAP_IS_IPV6 = 1<<3, | |
| NETCAP_HAS_EXT = 1<<6, | |
| NETCAP_DROP = 1<<7, | |
| }; | |
| //capture mode only, target bitstream | |
| u16 first_tcp_port=30000; | |
| #define GF_GPC_MAGIC GF_4CC('G','P','C', 0) | |
| //playback vars | |
| enum { | |
| NETCAP_NONE = 0, | |
| //read/write | |
| NETCAP_GPAC = 1, | |
| //read only | |
| NETCAP_PCAP = 2, | |
| NETCAP_PCAPNG = 3, | |
| }; | |
| typedef struct | |
| { | |
| u32 send_recv; | |
| u32 pck_start, pck_end; | |
| Bool pck_start_is_loop, pck_end_is_loop; | |
| s32 rand_every; | |
| u32 nb_pck; | |
| u32 port; | |
| s32 patch_offset, patch_val; | |
| s32 delay; | |
| } NetFilterRule; | |
| static GF_List *netcap_filters=NULL; | |
| #ifdef GPAC_HAS_FD | |
| GF_BitStream *gf_bs_from_fd(int fd, u32 mode); | |
| #endif | |
| struct __netcap_filter | |
| { | |
| char *id; | |
| char *src; | |
| char *dst; | |
| s32 loops; | |
| Bool rt; | |
| GF_List *rules; | |
| u32 nb_rules; | |
| u32 nb_reloads; | |
| //target bitstream for read/write | |
| GF_BitStream *cap_bs; | |
| //playback mode only, set of created sockets, used to discard packets on non-bound sockets | |
| GF_List *read_socks; | |
| //playback mode only, set to socket corresponding to pending packet | |
| GF_Socket *read_sock_selected; | |
| //playback/capture, FILE pointer | |
| FILE *file; | |
| #ifdef GPAC_HAS_FD | |
| //playback/capture, fd pointer | |
| int fd; | |
| #endif | |
| //playback only, clock init value for RT regulation | |
| u64 init_time; | |
| Bool is_eos; | |
| u64 pck_start_offset; | |
| //loaded packet size in bytes - this is the payload of the UDP/TCP packet - if 0, packet fetch failed | |
| s32 pck_len; | |
| //gpac format only, flags of loaded packet | |
| u8 pck_flags; | |
| //time of loaded packet, relative to sys_clock_high_res | |
| u64 pck_time; | |
| //destination port of loaded packet - if 0, new packet needs to be loaded | |
| u16 dst_port; | |
| //ipv4 dest address or mcast address of loaded packet | |
| u32 dst_v4; | |
| //ipv6 dest address or mcast address of loaded packet | |
| bin128 dst_v6; | |
| //source port of loaded packet | |
| u16 src_port; | |
| //ipv4 source address of loaded packet | |
| u32 src_v4; | |
| //ipv6 source address of loaded packet | |
| bin128 src_v6; | |
| //source file type | |
| u32 cap_mode; | |
| //for pcap files, indicate if little endian | |
| Bool pcap_le; | |
| //for pcap files, indicate nano seconds are used | |
| Bool pcap_nano; | |
| //number of interfaces currently defined - for pcap, always, 1 | |
| u32 pcap_num_interfaces; | |
| //all defined interfaces | |
| u32 link_types[20]; | |
| //trailing bytes in pcapng packet record | |
| u32 pcapng_trail; | |
| //trailing bytes after packet in pcap (NOT in pcapng) | |
| u32 pcap_trail; | |
| u32 pck_idx_r; | |
| u32 pck_idx_w; | |
| u32 next_pck_range; | |
| u32 next_rand; | |
| u32 reorder_max_pck, reorder_nb_pck, delay_nb_pck; | |
| u64 reorder_seek_pos, reorder_resume_pos; | |
| }; | |
| static GF_NetcapFilter *gf_net_filter_get(const char *id) | |
| { | |
| u32 i, count; | |
| count = gf_list_count(netcap_filters); | |
| for (i=0; i<count; i++) { | |
| GF_NetcapFilter *f = gf_list_get(netcap_filters, i); | |
| if (f->id && id && !strcmp(f->id, id)) | |
| return f; | |
| if (!f->id && !id) | |
| return f; | |
| } | |
| return NULL; | |
| } | |
| //end playback vars | |
| void gf_net_close_capture() | |
| { | |
| while (gf_list_count(netcap_filters)) { | |
| GF_NetcapFilter *nf = gf_list_pop_back(netcap_filters); | |
| if (nf->cap_bs) gf_bs_del(nf->cap_bs); | |
| #ifdef GPAC_HAS_FD | |
| if (nf->fd>=0) close(nf->fd); | |
| #endif | |
| if (nf->file) gf_fclose(nf->file); | |
| if (nf->read_socks) gf_list_del(nf->read_socks); | |
| if (nf->rules) { | |
| while (1) { | |
| NetFilterRule *r = gf_list_pop_back(nf->rules); | |
| if (!r) break; | |
| gf_free(r); | |
| } | |
| gf_list_del(nf->rules); | |
| } | |
| if (nf->id) gf_free(nf->id); | |
| if (nf->src) gf_free(nf->src); | |
| if (nf->dst) gf_free(nf->dst); | |
| gf_free(nf); | |
| } | |
| gf_list_del(netcap_filters); | |
| netcap_filters = NULL; | |
| } | |
| static GF_Err gf_netcap_record(GF_NetcapFilter *nf) | |
| { | |
| #ifdef GPAC_HAS_FD | |
| if (!gf_opts_get_bool("core", "no-fd")) { | |
| //make sure output dir exists | |
| gf_fopen(nf->dst, "mkdir"); | |
| nf->fd = gf_fd_open(nf->dst, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH ); | |
| if (nf->fd>=0) nf->cap_bs = gf_bs_from_fd(nf->fd, GF_BITSTREAM_WRITE); | |
| } else | |
| #endif | |
| { | |
| nf->file = gf_fopen_ex(nf->dst, NULL, "w+b", GF_FALSE); | |
| if (nf->file) nf->cap_bs = gf_bs_from_file(nf->file, GF_BITSTREAM_WRITE); | |
| } | |
| if (!nf->cap_bs) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[NetCap] Failed to setup net capture\n")); | |
| return GF_IO_ERR; | |
| } | |
| char *ext = gf_file_ext_start(nf->dst); | |
| if (ext && !strcmp(ext, ".pcap")) { | |
| nf->cap_mode = NETCAP_PCAP; | |
| gf_bs_write_u32(nf->cap_bs, 0xA1B2C3D4); | |
| gf_bs_write_u16(nf->cap_bs, 2); | |
| gf_bs_write_u16(nf->cap_bs, 4); | |
| gf_bs_write_u32(nf->cap_bs, 0); | |
| gf_bs_write_u32(nf->cap_bs, 0); | |
| gf_bs_write_u32(nf->cap_bs, 0xFFFF); //snapLen | |
| gf_bs_write_u16(nf->cap_bs, 0); //FCS+f+reserved | |
| gf_bs_write_u16(nf->cap_bs, 0); //linktype | |
| } else if (ext && !strcmp(ext, ".pcapng")) { | |
| nf->cap_mode = NETCAP_PCAPNG; | |
| //SHB | |
| gf_bs_write_u32(nf->cap_bs, 0x0a0d0d0a); | |
| gf_bs_write_u32(nf->cap_bs, 12+16); | |
| gf_bs_write_u32(nf->cap_bs, 0x1A2B3C4D); | |
| gf_bs_write_u16(nf->cap_bs, 1); | |
| gf_bs_write_u16(nf->cap_bs, 0); | |
| gf_bs_write_u64(nf->cap_bs, 0); | |
| gf_bs_write_u32(nf->cap_bs, 12+16); | |
| //IDB | |
| gf_bs_write_u32(nf->cap_bs, 1); | |
| gf_bs_write_u32(nf->cap_bs, 12+8); | |
| gf_bs_write_u16(nf->cap_bs, 0); //linktype | |
| gf_bs_write_u16(nf->cap_bs, 0); //reserved | |
| gf_bs_write_u32(nf->cap_bs, 0); //snapLen | |
| gf_bs_write_u32(nf->cap_bs, 12+8); | |
| } else { | |
| nf->cap_mode = NETCAP_GPAC; | |
| gf_bs_write_u32(nf->cap_bs, GF_GPC_MAGIC); | |
| nf->init_time = gf_sys_clock_high_res(); | |
| u32 sec, frac; | |
| gf_net_get_ntp(&sec, &frac); | |
| //NTP start time of capture | |
| gf_bs_write_u32(nf->cap_bs, sec); | |
| gf_bs_write_u32(nf->cap_bs, frac); | |
| //header extension size | |
| gf_bs_write_u32(nf->cap_bs, 0); | |
| } | |
| return GF_OK; | |
| } | |
| static void gf_netcap_load_pck_gpac(GF_NetcapFilter *nf) | |
| { | |
| nf->pck_start_offset = gf_bs_get_position(nf->cap_bs); | |
| if (gf_bs_available(nf->cap_bs)>4) | |
| nf->pck_len = gf_bs_read_u32(nf->cap_bs); | |
| else | |
| nf->pck_len = 0; | |
| //we allow size to be equal to our magic for file concat | |
| //this means pck size= 1196442368 (for v0) so we should be way over captured MTU | |
| if (nf->pck_len == GF_GPC_MAGIC) { | |
| /*ntp_sec = */gf_bs_read_u32(nf->cap_bs); | |
| /*ntp_frac = */gf_bs_read_u32(nf->cap_bs); | |
| u32 hdr_len = gf_bs_read_u32(nf->cap_bs); | |
| gf_bs_skip_bytes(nf->cap_bs, hdr_len); | |
| nf->pck_flags = NETCAP_DROP; | |
| nf->pck_len = 0; | |
| nf->dst_port = 1; | |
| return; | |
| } | |
| if (nf->pck_len<1+8+4+2) { | |
| nf->dst_v4 = 0; | |
| nf->dst_port = 0; | |
| nf->pck_len = 0; | |
| nf->is_eos = GF_TRUE; | |
| return; | |
| } | |
| nf->pck_len -= 1+8+2; | |
| //read flags | |
| nf->pck_flags = gf_bs_read_u8(nf->cap_bs); | |
| nf->pck_time = gf_bs_read_u64(nf->cap_bs); | |
| //extensions | |
| if (nf->pck_flags & NETCAP_HAS_EXT) { | |
| u16 xlen = gf_bs_read_u16(nf->cap_bs); | |
| gf_bs_skip_bytes(nf->cap_bs, xlen); | |
| nf->pck_len -= 2 + xlen; | |
| } | |
| //init clock on first packet | |
| if (!nf->init_time) { | |
| nf->init_time = gf_sys_clock_high_res(); | |
| nf->init_time -= nf->pck_time; | |
| } | |
| nf->pck_time += nf->init_time; | |
| if (nf->pck_flags & NETCAP_IS_IPV6) { | |
| nf->dst_v4 = 0; | |
| gf_bs_read_data(nf->cap_bs, nf->dst_v6, 16); | |
| nf->pck_len -= 16; | |
| if (nf->pck_flags & NETCAP_HAS_PEER) { | |
| gf_bs_read_data(nf->cap_bs, nf->src_v6, 16); | |
| nf->pck_len -= 16; | |
| } | |
| } else { | |
| nf->dst_v4 = gf_bs_read_u32(nf->cap_bs); | |
| nf->pck_len -= 4; | |
| if (nf->pck_flags & NETCAP_HAS_PEER) { | |
| nf->src_v4 = gf_bs_read_u32(nf->cap_bs); | |
| nf->pck_len -= 4; | |
| } | |
| } | |
| nf->dst_port = gf_bs_read_u16(nf->cap_bs); | |
| if (nf->pck_flags & NETCAP_HAS_PEER) { | |
| nf->src_port = gf_bs_read_u16(nf->cap_bs); | |
| nf->pck_len -= 2; | |
| } else { | |
| nf->src_port = 0; | |
| } | |
| if (gf_bs_is_overflow(nf->cap_bs)) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[NetCap] Corrupted netcap file, aborting\n")); | |
| nf->pck_len = -1; | |
| } | |
| //broken packet | |
| if (nf->pck_len <0) { | |
| nf->dst_v4 = 0; | |
| nf->dst_port = 0; | |
| nf->pck_len = 0; | |
| nf->is_eos = GF_TRUE; | |
| } | |
| } | |
| enum | |
| { | |
| PCAP_LT_LOOPBACK=0, | |
| PCAP_LT_ETHERNET=1, | |
| PCAP_LT_CHAOS=5 | |
| }; | |
| static GF_Err pcapng_load_shb(GF_NetcapFilter *nf, Bool is_init) | |
| { | |
| u32 blen = gf_bs_read_u32(nf->cap_bs); | |
| u32 le_magic = gf_bs_read_u32(nf->cap_bs); | |
| if (le_magic == GF_4CC(0x1A, 0x2B, 0x3C, 0x4D)) { | |
| } else if (le_magic == GF_4CC(0x4D, 0x3C, 0x2B, 0x1A)) { | |
| nf->pcap_le = GF_TRUE; | |
| blen = ntohl(blen); | |
| } else { | |
| if (is_init) | |
| gf_net_close_capture(); | |
| else | |
| nf->is_eos = GF_TRUE; | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[NetCap] Corrrupted pacpng file\n")); | |
| return GF_NON_COMPLIANT_BITSTREAM; | |
| } | |
| gf_bs_skip_bytes(nf->cap_bs, blen-12); | |
| nf->pcap_num_interfaces = 0; | |
| return GF_OK; | |
| } | |
| static void pcapng_load_idb(GF_NetcapFilter *nf) | |
| { | |
| if (nf->pcap_num_interfaces>=20) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[NetCap] Maximum 20 concurrent interfaces supported in pacpng, skipping packets\n")); | |
| return; | |
| } | |
| u32 blen = nf->pcap_le ? gf_bs_read_u32_le(nf->cap_bs) : gf_bs_read_u32(nf->cap_bs); | |
| nf->link_types[nf->pcap_num_interfaces] = nf->pcap_le ? gf_bs_read_u16_le(nf->cap_bs) : gf_bs_read_u16(nf->cap_bs); | |
| gf_bs_skip_bytes(nf->cap_bs, blen-10); | |
| switch (nf->link_types[nf->pcap_num_interfaces]) { | |
| case PCAP_LT_LOOPBACK: | |
| case PCAP_LT_ETHERNET: | |
| case PCAP_LT_CHAOS: | |
| break; | |
| default: | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[NetCap] Only ethernet and BSD-loopback pcap files are supported, skipping packets\n")); | |
| break; | |
| } | |
| nf->pcap_num_interfaces++; | |
| } | |
| #define SKIP_PCAP_PCK \ | |
| if (clen<0) {\ | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[NetCap] Corrupted PCAP file, aborting\n"));\ | |
| nf->is_eos = GF_TRUE;\ | |
| return;\ | |
| }\ | |
| gf_bs_skip_bytes(nf->cap_bs, clen);\ | |
| clen=0;\ | |
| goto refetch_pcap; | |
| static void gf_netcap_load_pck_pcap(GF_NetcapFilter *nf) | |
| { | |
| u32 s, frac; | |
| s32 clen=0, bsize=0; | |
| refetch_pcap: | |
| nf->dst_port = 0; | |
| nf->pck_len = 0; | |
| if (nf->pcapng_trail) { | |
| gf_bs_skip_bytes(nf->cap_bs, nf->pcapng_trail); | |
| nf->pcapng_trail = 0; | |
| } else if (nf->pcap_trail) { | |
| gf_bs_skip_bytes(nf->cap_bs, nf->pcapng_trail); | |
| nf->pcap_trail = 0; | |
| } | |
| if (gf_bs_available(nf->cap_bs)<16) { | |
| nf->is_eos = GF_TRUE; | |
| return; | |
| } | |
| nf->pck_start_offset = gf_bs_get_position(nf->cap_bs); | |
| u32 link_type; | |
| if (nf->cap_mode==NETCAP_PCAPNG) { | |
| u32 btype = nf->pcap_le ? gf_bs_read_u32_le(nf->cap_bs) : gf_bs_read_u32(nf->cap_bs); | |
| if (btype==GF_4CC(0x0A, 0x0D, 0x0D, 0x0A)) { | |
| GF_Err e = pcapng_load_shb(nf, GF_FALSE); | |
| //if failure, nf is destroyed by gf_net_close_capture | |
| if (e) { | |
| return; | |
| } | |
| goto refetch_pcap; | |
| } else if (btype==0x00000001) { | |
| pcapng_load_idb(nf); | |
| goto refetch_pcap; | |
| } | |
| bsize = nf->pcap_le ? gf_bs_read_u32_le(nf->cap_bs) : gf_bs_read_u32(nf->cap_bs); | |
| if (bsize <= 8) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[NetCap] Corrupted PCAP file, aborting\n")); | |
| nf->is_eos = GF_TRUE; | |
| return; | |
| } | |
| if (btype!=0x00000006) { | |
| gf_bs_skip_bytes(nf->cap_bs, bsize-8); | |
| nf->pcapng_trail = 0; | |
| goto refetch_pcap; | |
| } | |
| u32 ifce_idx = nf->pcap_le ? gf_bs_read_u32_le(nf->cap_bs) : gf_bs_read_u32(nf->cap_bs); | |
| if (ifce_idx<nf->pcap_num_interfaces) | |
| link_type = nf->link_types[0]; | |
| else | |
| link_type = 0xFF; | |
| } else { | |
| link_type = nf->link_types[0]; | |
| } | |
| if (nf->pcap_le) { | |
| s = gf_bs_read_u32_le(nf->cap_bs); | |
| frac = gf_bs_read_u32_le(nf->cap_bs); | |
| clen = gf_bs_read_u32_le(nf->cap_bs); | |
| /*olen = */gf_bs_read_u32_le(nf->cap_bs); | |
| } else { | |
| s = gf_bs_read_u32(nf->cap_bs); | |
| frac = gf_bs_read_u32(nf->cap_bs); | |
| clen = gf_bs_read_u32(nf->cap_bs); | |
| /*olen = */gf_bs_read_u32(nf->cap_bs); | |
| } | |
| if (clen<0) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[NetCap] Corrupted PCAP file, aborting\n")); | |
| nf->is_eos = GF_TRUE; | |
| return; | |
| } | |
| if (nf->cap_mode==NETCAP_PCAPNG) { | |
| nf->pcapng_trail = bsize - 28 - clen; | |
| } | |
| Bool ip_v6=GF_FALSE; | |
| if (link_type==0) { | |
| u32 type = nf->pcap_le ? gf_bs_read_u32_le(nf->cap_bs) : gf_bs_read_u32(nf->cap_bs); | |
| clen-=4; | |
| if (type==2) { | |
| } else if ((type==24) || (type==28) || (type==30)) { | |
| ip_v6=GF_TRUE; | |
| } else { | |
| SKIP_PCAP_PCK | |
| } | |
| } | |
| //Ethernet | |
| else if (link_type==1) { | |
| gf_bs_skip_bytes(nf->cap_bs, 12); | |
| u32 etype = gf_bs_read_u16(nf->cap_bs); | |
| clen-=14; | |
| if (etype==0x0800) { | |
| } else if (etype==0x86DD) { | |
| ip_v6=GF_TRUE; | |
| } else { | |
| SKIP_PCAP_PCK | |
| } | |
| } | |
| //raw IPv4 | |
| else if (link_type==5) { | |
| } | |
| //not supported | |
| else { | |
| gf_bs_skip_bytes(nf->cap_bs, clen); | |
| goto refetch_pcap; | |
| } | |
| u8 protocol_type=0; | |
| if (ip_v6) { | |
| gf_bs_skip_bytes(nf->cap_bs, 4); | |
| /*pay_len = */gf_bs_read_u16(nf->cap_bs); | |
| u32 next_hdr = gf_bs_read_u8(nf->cap_bs); | |
| gf_bs_read_u8(nf->cap_bs); | |
| gf_bs_read_data(nf->cap_bs, nf->src_v6, 16); | |
| gf_bs_read_data(nf->cap_bs, nf->dst_v6, 16); | |
| clen -= 40; | |
| Bool has_hdr = 1; | |
| while (has_hdr) { | |
| u32 hdr_size; | |
| switch (next_hdr) { | |
| case 0: | |
| case 60: | |
| case 43: | |
| case 44: | |
| case 50: | |
| case 51: | |
| case 135: | |
| case 139: | |
| case 140: | |
| next_hdr = gf_bs_read_u8(nf->cap_bs); | |
| hdr_size = gf_bs_read_u8(nf->cap_bs); | |
| gf_bs_skip_bytes(nf->cap_bs, 2 + 8*hdr_size); | |
| hdr_size = 8*(hdr_size+1); | |
| clen -= hdr_size; | |
| break; | |
| case 6: | |
| case 17: | |
| protocol_type=next_hdr; | |
| has_hdr=0; | |
| break; | |
| default: | |
| SKIP_PCAP_PCK | |
| } | |
| if (gf_bs_is_overflow(nf->cap_bs)) { | |
| break; | |
| } | |
| } | |
| } else { | |
| gf_bs_skip_bytes(nf->cap_bs, 9); | |
| protocol_type = gf_bs_read_u8(nf->cap_bs); | |
| gf_bs_skip_bytes(nf->cap_bs, 2); | |
| nf->src_v4 = gf_bs_read_u32_le(nf->cap_bs); | |
| nf->dst_v4 = gf_bs_read_u32_le(nf->cap_bs); | |
| clen-=20; | |
| } | |
| //UDP | |
| if (protocol_type==17) { | |
| nf->src_port = gf_bs_read_u16(nf->cap_bs); | |
| nf->dst_port = gf_bs_read_u16(nf->cap_bs); | |
| gf_bs_skip_bytes(nf->cap_bs, 4); | |
| clen-=8; | |
| } | |
| //TCP | |
| else if (protocol_type==6) { | |
| nf->src_port = gf_bs_read_u16(nf->cap_bs); | |
| nf->dst_port = gf_bs_read_u16(nf->cap_bs); | |
| /*sn = */gf_bs_read_u32(nf->cap_bs); | |
| /*ack = */gf_bs_read_u32(nf->cap_bs); | |
| u32 data_offset = gf_bs_read_int(nf->cap_bs, 4); | |
| gf_bs_read_int(nf->cap_bs, 4); | |
| gf_bs_skip_bytes(nf->cap_bs, 7); | |
| if (data_offset>5) | |
| gf_bs_skip_bytes(nf->cap_bs, (data_offset-5)*4); | |
| clen -= data_offset*4; | |
| } else { | |
| //no support | |
| SKIP_PCAP_PCK | |
| } | |
| if ((clen<0)|| gf_bs_is_overflow(nf->cap_bs)) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[NetCap] Corrupted PCAP file, aborting\n")); | |
| nf->is_eos = GF_TRUE; | |
| return; | |
| } | |
| //for pcap, skip ethernet CRC | |
| if ((nf->cap_mode==NETCAP_PCAP) && (link_type==1)) | |
| nf->pcap_trail = 4; | |
| if (nf->cap_mode==NETCAP_PCAP) { | |
| nf->pck_time = s; | |
| nf->pck_time *= 1000000; | |
| if (nf->pcap_nano) | |
| nf->pck_time+=frac/1000; | |
| else | |
| nf->pck_time+=frac; | |
| } else { | |
| nf->pck_time = s; | |
| nf->pck_time <<= 32; | |
| nf->pck_time |= frac; | |
| //in microsec since UTC epoch | |
| } | |
| if (!nf->init_time) { | |
| nf->init_time = gf_sys_clock_high_res(); | |
| nf->init_time -= nf->pck_time; | |
| } | |
| nf->pck_time += nf->init_time; | |
| nf->pck_len = clen; | |
| if (!clen) goto refetch_pcap; | |
| } | |
| static GF_Err gf_netcap_playback(GF_NetcapFilter *nf); | |
| GF_Err gf_netcap_setup(char *rules) | |
| { | |
| char *id=NULL; | |
| char *src=NULL; | |
| char *dst=NULL; | |
| s32 cap_loop=0; | |
| Bool cap_rt=GF_TRUE; | |
| char *rsep = strchr(rules, '['); | |
| if (rsep) rsep[0] = 0; | |
| //extract src or dst, nrt flag and loop flag | |
| while (rules) { | |
| char *sep = strchr(rules, ','); | |
| if (sep) sep[0] = 0; | |
| if (!strncmp(rules, "id=", 3)) { | |
| id = gf_strdup(rules+3); | |
| } else if (!strncmp(rules, "src=", 4)) { | |
| src = gf_strdup(rules+4); | |
| } else if (!strncmp(rules, "dst=", 4)) { | |
| dst = gf_strdup(rules+4); | |
| } else if (!strcmp(rules, "loop")) { | |
| cap_loop = -1; | |
| } else if (!strncmp(rules, "loop=", 5)) { | |
| cap_loop = atoi(rules+5); | |
| } else if (!strcmp(rules, "nrt")) { | |
| cap_rt = GF_FALSE; | |
| } | |
| if (!sep) { | |
| rules=NULL; | |
| break; | |
| } | |
| sep[0] = ','; | |
| rules = sep+1; | |
| if (rules[0]=='[') break; | |
| } | |
| if (rsep) { | |
| rsep[0] = '['; | |
| rules = rsep; | |
| } | |
| if (!dst && !src && !rules) { | |
| if (id) gf_free(id); | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[NetCap] Invalid netcap rule, ignoring\n")); | |
| return GF_OK; | |
| } | |
| if (dst && src) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[NetCap] Invalid netcap, src and dst cannot be set at the same time\n")); | |
| if (id) gf_free(id); | |
| gf_free(src); | |
| gf_free(dst); | |
| return GF_BAD_PARAM; | |
| } | |
| GF_NetcapFilter *nf = gf_net_filter_get(id); | |
| if (nf) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[NetCap] Several netcap filters with same IDs are not supported, ignoring\n")); | |
| if (id) gf_free(id); | |
| if (src) gf_free(src); | |
| if (dst) gf_free(dst); | |
| return GF_BAD_PARAM; | |
| } | |
| GF_SAFEALLOC(nf, GF_NetcapFilter); | |
| if (!nf) { | |
| if (id) gf_free(id); | |
| if (src) gf_free(src); | |
| if (dst) gf_free(dst); | |
| return GF_OUT_OF_MEM; | |
| } | |
| nf->id = id; | |
| nf->src = src; | |
| nf->dst = dst; | |
| nf->loops = cap_loop; | |
| nf->rt = cap_rt; | |
| nf->rules = gf_list_new(); | |
| if (!nf->rules) { | |
| if (id) gf_free(id); | |
| if (src) gf_free(src); | |
| if (dst) gf_free(dst); | |
| gf_free(nf); | |
| return GF_OUT_OF_MEM; | |
| } | |
| if (!netcap_filters) netcap_filters = gf_list_new(); | |
| gf_list_add(netcap_filters, nf); | |
| #ifdef GPAC_HAS_FD | |
| nf->fd = -1; | |
| #endif | |
| while (rules) { | |
| u32 send_recv = 1; //default reception only | |
| u32 port=0; | |
| u32 num_pck=1; | |
| u32 pck_start=0; | |
| u32 pck_end=0; | |
| s32 patch_offset=-1; | |
| s32 patch_val=-1; | |
| s32 rand_every = 0; | |
| s32 delay = 0; | |
| Bool pck_start_is_loop = GF_FALSE; | |
| Bool pck_end_is_loop = GF_FALSE; | |
| char *rule_str; | |
| char *sep = strchr(rules, '['); | |
| if (!sep) break; | |
| sep = strchr(sep+1, ']'); | |
| if (!sep) break; | |
| sep[0] = 0; | |
| rule_str = rules+1; | |
| while (rule_str[1]=='=') { | |
| char *sep2 = strchr(rule_str+2, ','); | |
| if (sep2) sep2[0]=0; | |
| switch (rule_str[0]) { | |
| case 'm': | |
| if (!strcmp(rule_str+2, "r")) send_recv = 1; | |
| else if (!strcmp(rule_str+2, "w")) send_recv = 2; | |
| else if (!strcmp(rule_str+2, "rw") || !strcmp(rule_str+2, "wr")) send_recv = 0; | |
| else { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[NetCap] Unknown mode %s, using read/write\n", rule_str+2)); | |
| send_recv=0; | |
| } | |
| break; | |
| case 'p': | |
| port = atoi(rule_str+2); | |
| break; | |
| case 'S': | |
| pck_start_is_loop = GF_TRUE; | |
| case 's': | |
| pck_start = atoi(rule_str+2); | |
| break; | |
| case 'E': | |
| pck_end_is_loop = GF_TRUE; | |
| case 'e': | |
| pck_end = atoi(rule_str+2); | |
| break; | |
| case 'n': | |
| num_pck = atoi(rule_str+2); | |
| break; | |
| case 'r': | |
| rand_every = atoi(rule_str+2); | |
| if (rand_every<0) rand_every = -rand_every; | |
| break; | |
| case 'f': | |
| rand_every = atoi(rule_str+2); | |
| if (rand_every>0) rand_every = -rand_every; | |
| break; | |
| case 'o': | |
| patch_offset = atoi(rule_str+2); | |
| break; | |
| case 'v': | |
| if (strchr(rule_str, '-')) patch_val =-1; | |
| else patch_val = strtol(rule_str+2, NULL, 16); | |
| break; | |
| case 'd': | |
| if (!nf->src) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[NetCap] Reorder rule only supported from pcap playback, ignoring\n")); | |
| break; | |
| } | |
| delay = atoi(rule_str+2); | |
| break; | |
| default: | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[NetCap] Unknown directive %c, ignoring\n", rule_str[0])); | |
| } | |
| if (!sep2) break; | |
| sep2[0] = ','; | |
| rule_str = sep2+1; | |
| } | |
| NetFilterRule *rule; | |
| GF_SAFEALLOC(rule, NetFilterRule); | |
| rule->send_recv = send_recv; | |
| rule->port = port; | |
| rule->patch_offset = patch_offset; | |
| rule->patch_val = patch_val; | |
| rule->pck_start = pck_start; | |
| rule->pck_end = pck_end>pck_start ? pck_end : 0; | |
| rule->pck_start_is_loop = pck_start_is_loop; | |
| rule->pck_end_is_loop = pck_end_is_loop; | |
| rule->rand_every = rand_every; | |
| rule->nb_pck = num_pck; | |
| rule->delay = delay; | |
| gf_list_add(nf->rules, rule); | |
| sep[0] = ']'; | |
| rules = sep+1; | |
| } | |
| nf->nb_rules = gf_list_count(nf->rules); | |
| if (nf->dst) { | |
| GF_Err e = gf_netcap_record(nf); | |
| if (e) return e; | |
| } | |
| if (nf->src) { | |
| GF_Err e = gf_netcap_playback(nf); | |
| if (e) return e; | |
| } | |
| return GF_OK; | |
| } | |
| static Bool netcap_filter_pck(GF_Socket *sock, u32 pck_len, Bool for_send) | |
| { | |
| u32 i=0; | |
| if (!sock || !sock->cap_info) return GF_FALSE; | |
| GF_NetcapFilter *nf = sock->cap_info->nf; | |
| if (for_send) { | |
| nf->pck_idx_w++; | |
| sock->cap_info->pck_idx_w++; | |
| } else { | |
| nf->pck_idx_r++; | |
| sock->cap_info->pck_idx_r++; | |
| } | |
| for (i=0; i<nf->nb_rules; i++) { | |
| NetFilterRule *r = gf_list_get(nf->rules, i); | |
| if (r->port && (r->port != nf->dst_port)) continue; | |
| if (for_send) { | |
| if (r->send_recv==1) continue; | |
| } else { | |
| if (r->send_recv==2) continue; | |
| } | |
| NetCapInfo *ci = r->port ? sock->cap_info : NULL; | |
| u32 cur_pck; | |
| if (ci) { | |
| cur_pck = for_send ? ci->pck_idx_w : ci->pck_idx_r; | |
| } else { | |
| cur_pck = for_send ? nf->pck_idx_w : nf->pck_idx_r; | |
| } | |
| //check range | |
| u32 pck_start = r->pck_start; | |
| if (r->pck_start && r->pck_start_is_loop) | |
| pck_start += nf->nb_reloads; | |
| if (!pck_start && r->pck_start_is_loop) continue; | |
| u32 pck_end = pck_start ? r->pck_end : 0; | |
| if (r->pck_end && r->pck_end_is_loop) | |
| pck_end += nf->nb_reloads; | |
| if (pck_start && (cur_pck < pck_start)) continue; | |
| if (pck_end && (cur_pck >= pck_end)) continue; | |
| //not repeated pattern case | |
| if (!r->rand_every) { | |
| if ((cur_pck >= pck_start) && (pck_start + r->nb_pck > cur_pck)) {} | |
| else continue; | |
| } | |
| //repeated pattern case | |
| else { | |
| u32 next_pck_range = ci ? ci->next_pck_range : nf->next_pck_range; | |
| u32 next_rand = ci ? ci->next_rand : nf->next_rand; | |
| //in range, check if we need to recompute | |
| if (next_pck_range < pck_start) | |
| next_pck_range = pck_start; | |
| if (next_pck_range <= cur_pck) { | |
| if (r->rand_every>0) { | |
| next_rand = next_pck_range + gf_rand() % r->rand_every; | |
| next_pck_range += r->rand_every; | |
| } else { | |
| next_rand = next_pck_range; | |
| next_pck_range += -r->rand_every; | |
| } | |
| if (ci) { | |
| ci->next_pck_range = next_pck_range; | |
| ci->next_rand = next_rand; | |
| } else { | |
| nf->next_pck_range = next_pck_range; | |
| nf->next_rand = next_rand; | |
| } | |
| } | |
| //ours | |
| if (r->nb_pck) { | |
| if ((next_rand<=cur_pck) && (next_rand+r->nb_pck>cur_pck)) {} | |
| else continue; | |
| } else { | |
| if (next_rand==cur_pck) {} | |
| else continue; | |
| } | |
| } | |
| if ((r->patch_offset==-1) && !(sock->flags & GF_SOCK_IS_TCP)) { | |
| if (r->delay) { | |
| if (!nf->reorder_seek_pos) { | |
| nf->reorder_seek_pos = nf->pck_start_offset; | |
| nf->reorder_max_pck = r->nb_pck; | |
| nf->delay_nb_pck = r->delay; | |
| nf->reorder_nb_pck = 0; | |
| } | |
| nf->reorder_nb_pck ++; | |
| return GF_TRUE; | |
| } | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[NetCap] Dropping packet %d\n", cur_pck)); | |
| return GF_TRUE; | |
| } | |
| u32 bo = (r->patch_offset>=0) ? r->patch_offset : gf_rand(); | |
| bo = bo % pck_len; | |
| sock->cap_info->patch_offset = 1+bo; | |
| u32 val = (r->patch_val>=0) ? r->patch_val : gf_rand(); | |
| val = val % 256; | |
| sock->cap_info->patch_val = val; | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[NetCap] Patching packet %d byte offset %d to 0x%02X\n", cur_pck, bo, val)); | |
| // we want to patch many bytes of same packet | |
| // return GF_FALSE; | |
| } | |
| return GF_FALSE; | |
| } | |
| static void gf_netcap_load_pck(GF_NetcapFilter *nf, u64 now) | |
| { | |
| refetch: | |
| //no packet loaded | |
| if (!nf->dst_port) { | |
| Bool discard=GF_FALSE; | |
| nf->pck_flags=0; | |
| nf->read_sock_selected = NULL; | |
| if (nf->loops && !gf_bs_available(nf->cap_bs)) { | |
| nf->nb_reloads++; | |
| if (nf->loops>0) | |
| nf->loops--; | |
| gf_bs_seek(nf->cap_bs, 0); | |
| nf->init_time = 0; | |
| if (nf->cap_mode==NETCAP_PCAP) | |
| gf_bs_skip_bytes(nf->cap_bs, 24); | |
| } | |
| if (nf->reorder_seek_pos) { | |
| //we passed all packets to reorder | |
| if (nf->reorder_nb_pck == nf->reorder_max_pck) { | |
| if (!nf->delay_nb_pck) { | |
| //remember pos after reorder | |
| nf->reorder_resume_pos = gf_bs_get_position(nf->cap_bs); | |
| gf_bs_seek(nf->cap_bs, nf->reorder_seek_pos); | |
| nf->reorder_seek_pos = 0; | |
| nf->pcapng_trail = nf->pcap_trail = 0; | |
| nf->reorder_nb_pck = 1; | |
| } else { | |
| nf->delay_nb_pck--; | |
| } | |
| } | |
| } else if (nf->reorder_resume_pos) { | |
| if (nf->reorder_nb_pck == nf->reorder_max_pck) { | |
| gf_bs_seek(nf->cap_bs, nf->reorder_resume_pos); | |
| nf->reorder_resume_pos = 0; | |
| nf->pcapng_trail = nf->pcap_trail = 0; | |
| nf->reorder_nb_pck = 0; | |
| } else { | |
| nf->reorder_nb_pck++; | |
| } | |
| } | |
| if (nf->cap_mode==NETCAP_GPAC) | |
| gf_netcap_load_pck_gpac(nf); | |
| else | |
| gf_netcap_load_pck_pcap(nf); | |
| //eof | |
| if (!nf->dst_port) return; | |
| //force discard | |
| if ((nf->pck_flags & NETCAP_DROP)) discard = GF_TRUE; | |
| if (discard) { | |
| gf_bs_skip_bytes(nf->cap_bs, nf->pck_len); | |
| nf->dst_port = 0; | |
| goto refetch; | |
| } | |
| } | |
| if (nf->rt && now && (nf->pck_time > now)) { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[NetCap] Packet too early by %d ms\n", (u32) (nf->pck_time - now) / 1000)); | |
| return; | |
| } | |
| //we already have the socket | |
| if (nf->read_sock_selected) | |
| return; | |
| //find destination socket | |
| u32 i=0; | |
| GF_Socket *s = NULL; | |
| while ((s=gf_list_enum(nf->read_socks, &i))) { | |
| //in pcap mode, check multicast - if enabled, match dst | |
| //in GPC mode with multicast, only multicast address is used, in host port/addr fields | |
| if ((nf->cap_mode!=NETCAP_GPAC) && (s->flags & GF_SOCK_IS_MULTICAST)) { | |
| if (s->cap_info->peer_port != nf->dst_port) | |
| continue; | |
| if (nf->pck_flags & NETCAP_IS_IPV6) { | |
| if (memcmp(s->cap_info->peer_addr_v6, nf->dst_v6, 16)) continue; | |
| } else { | |
| if (s->cap_info->peer_addr_v4 != nf->dst_v4) continue; | |
| } | |
| nf->read_sock_selected = s; | |
| s->cap_info->patch_offset = 0; | |
| break; | |
| } | |
| if (s->cap_info->host_port != nf->dst_port) | |
| continue; | |
| if (nf->pck_flags & NETCAP_IS_IPV6) { | |
| if (memcmp(s->cap_info->host_addr_v6, nf->dst_v6, 16)) continue; | |
| } else { | |
| //accept INADDR_ANY | |
| if (s->cap_info->host_addr_v4 && (s->cap_info->host_addr_v4 != nf->dst_v4)) continue; | |
| if (s->flags & GF_SOCK_IS_TCP) { | |
| if (s->cap_info->peer_addr_v4 != nf->src_v4) continue; | |
| if (!s->cap_info->peer_port) { | |
| s->cap_info->peer_port = nf->src_port; | |
| } else if (s->cap_info->peer_port != nf->src_port) { | |
| continue; | |
| } | |
| } | |
| } | |
| nf->read_sock_selected = s; | |
| s->cap_info->patch_offset = 0; | |
| break; | |
| } | |
| //apply rule(s) except when sending delayed packets | |
| if (!nf->reorder_resume_pos && netcap_filter_pck(nf->read_sock_selected, nf->pck_len, GF_FALSE)) | |
| nf->read_sock_selected = NULL; | |
| if (!nf->read_sock_selected) { | |
| gf_bs_skip_bytes(nf->cap_bs, nf->pck_len); | |
| nf->dst_port = 0; | |
| goto refetch; | |
| } | |
| } | |
| static GF_Err gf_netcap_playback(GF_NetcapFilter *nf) | |
| { | |
| #ifdef GPAC_HAS_FD | |
| if (!gf_opts_get_bool("core", "no-fd")) { | |
| nf->fd = gf_fd_open(nf->src, O_RDONLY | O_BINARY , S_IRUSR | S_IWUSR); | |
| if (nf->fd>=0) nf->cap_bs = gf_bs_from_fd(nf->fd, GF_BITSTREAM_READ); | |
| } else | |
| #endif | |
| { | |
| nf->file = gf_fopen_ex(nf->src, NULL, "rb", GF_FALSE); | |
| if (nf->file) nf->cap_bs = gf_bs_from_file(nf->file, GF_BITSTREAM_READ); | |
| } | |
| if (!nf->cap_bs) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[NetCap] Failed to setup capture playback\n")); | |
| return GF_IO_ERR; | |
| } | |
| nf->read_socks = gf_list_new(); | |
| u32 magic = gf_bs_read_u32(nf->cap_bs); | |
| if (magic == GF_GPC_MAGIC) { | |
| nf->cap_mode = NETCAP_GPAC; | |
| } else if (magic==GF_4CC(0xD4, 0xC3, 0xB2, 0xA1)) { | |
| nf->cap_mode = NETCAP_PCAP; | |
| nf->pcap_le = GF_TRUE; | |
| } else if (magic==GF_4CC(0xA1, 0xB2, 0xC3, 0xD4)) { | |
| nf->cap_mode = NETCAP_PCAP; | |
| } else if (magic==GF_4CC(0x4D, 0x3C, 0xB2, 0xA1)) { | |
| nf->cap_mode = NETCAP_PCAP; | |
| nf->pcap_nano = GF_TRUE; | |
| nf->pcap_le = GF_TRUE; | |
| } else if (magic==GF_4CC(0xA1, 0xB2, 0x3C, 0x4D)) { | |
| nf->cap_mode = NETCAP_PCAP; | |
| nf->pcap_nano = GF_TRUE; | |
| } else if (magic==GF_4CC(0x0A, 0x0D, 0x0D, 0x0A)) { | |
| nf->cap_mode = NETCAP_PCAPNG; | |
| nf->pcap_nano = GF_TRUE; | |
| } else { | |
| gf_net_close_capture(); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[NetCap] Unsupported capture file magic %s\n", gf_4cc_to_str(magic))); | |
| return GF_NOT_SUPPORTED; | |
| } | |
| if (nf->cap_mode==NETCAP_GPAC) { | |
| /*ntp_sec = */gf_bs_read_u32(nf->cap_bs); | |
| /*ntp_frac = */gf_bs_read_u32(nf->cap_bs); | |
| magic = gf_bs_read_u32(nf->cap_bs); | |
| gf_bs_skip_bytes(nf->cap_bs, magic); | |
| return GF_OK; | |
| } | |
| if (nf->cap_mode==NETCAP_PCAP) { | |
| gf_bs_skip_bytes(nf->cap_bs, 16); //we ignore snapLen | |
| gf_bs_read_int(nf->cap_bs, 4); | |
| nf->link_types[0] = gf_bs_read_int(nf->cap_bs, 28); | |
| if (nf->pcap_le) nf->link_types[0] = ntohl(nf->link_types[0]); | |
| switch (nf->link_types[0]) { | |
| case PCAP_LT_LOOPBACK: | |
| case PCAP_LT_ETHERNET: | |
| case PCAP_LT_CHAOS: | |
| break; | |
| default: | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[NetCap] Only ethernet and BSD-loopback pcap files are supported, got %u\n", nf->link_types[0])); | |
| gf_net_close_capture(); | |
| return GF_NOT_SUPPORTED; | |
| } | |
| } | |
| //pcapng | |
| else if (nf->cap_mode==NETCAP_PCAPNG) { | |
| GF_Err e = pcapng_load_shb(nf, GF_TRUE); | |
| if (e) return e; | |
| u32 btype = nf->pcap_le ? gf_bs_read_u32_le(nf->cap_bs) : gf_bs_read_u32(nf->cap_bs); | |
| if (btype != 1) { | |
| gf_net_close_capture(); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[NetCap] Missing IDB block after pacpng header\n")); | |
| return GF_NON_COMPLIANT_BITSTREAM; | |
| } | |
| pcapng_load_idb(nf); | |
| } | |
| return GF_OK; | |
| } | |
| static GF_Err gf_netcap_send_pcap(GF_NetcapFilter *nf, GF_Socket *sock, const u8 *buffer, u32 length, u32 *written, Bool is_ng) | |
| { | |
| u32 sec, frac, pck_len, ng_size=0, ng_trail=0; | |
| if (!sock->cap_info->host_port) return GF_BAD_PARAM; | |
| pck_len = length; | |
| pck_len += 4; | |
| if (sock->flags & GF_SOCK_IS_IPV6) { | |
| pck_len += 40; | |
| } else { | |
| pck_len += 20; | |
| } | |
| if (sock->flags & GF_SOCK_IS_TCP) { | |
| pck_len += 20; | |
| } else { | |
| pck_len += 8; | |
| } | |
| if (is_ng) { | |
| ng_size = pck_len/4; | |
| ng_trail = pck_len%4; | |
| if (ng_trail) { | |
| ng_size++; | |
| ng_trail = 4-ng_trail; | |
| } | |
| ng_size += 5; | |
| ng_size *= 4; | |
| ng_size+=12; | |
| gf_bs_write_u32(nf->cap_bs, 6); | |
| gf_bs_write_u32(nf->cap_bs, ng_size); | |
| gf_bs_write_u32(nf->cap_bs, 0); | |
| } | |
| gf_net_get_ntp(&sec, &frac); | |
| gf_bs_write_u32(nf->cap_bs, sec); | |
| gf_bs_write_u32(nf->cap_bs, (u32) gf_timestamp_rescale(frac, 0xFFFFFFFF, 1000000)); | |
| gf_bs_write_u32(nf->cap_bs, pck_len); | |
| gf_bs_write_u32(nf->cap_bs, pck_len); | |
| gf_bs_write_u32(nf->cap_bs, (sock->flags & GF_SOCK_IS_IPV6) ? 24 : 2); | |
| pck_len-=4; | |
| u8 protocol_type = (sock->flags & GF_SOCK_IS_TCP) ? 6 : 17; | |
| if (sock->flags & GF_SOCK_IS_IPV6) { | |
| gf_bs_write_int(nf->cap_bs, 6, 4); | |
| gf_bs_write_int(nf->cap_bs, 0, 4); | |
| gf_bs_write_u24(nf->cap_bs, 0); | |
| gf_bs_write_u16(nf->cap_bs, pck_len); | |
| gf_bs_write_u8(nf->cap_bs, protocol_type); | |
| gf_bs_write_u8(nf->cap_bs, 10); | |
| gf_bs_write_data(nf->cap_bs, sock->cap_info->host_addr_v6, 16); | |
| gf_bs_write_data(nf->cap_bs, sock->cap_info->peer_addr_v6, 16); | |
| } else { | |
| u32 chk_sum = 0x4500; | |
| gf_bs_write_int(nf->cap_bs, 4, 4); | |
| gf_bs_write_int(nf->cap_bs, 5, 4); | |
| gf_bs_write_u8(nf->cap_bs, 0); | |
| chk_sum += pck_len; | |
| gf_bs_write_u16(nf->cap_bs, pck_len); | |
| gf_bs_skip_bytes(nf->cap_bs, 4); | |
| gf_bs_write_u8(nf->cap_bs, 10); | |
| gf_bs_write_u8(nf->cap_bs, protocol_type); | |
| chk_sum += (0x0a00 | protocol_type); | |
| chk_sum += ((sock->cap_info->host_addr_v4&0xFF)<<8) | ((sock->cap_info->host_addr_v4>>8)&0xFF); | |
| chk_sum += (((sock->cap_info->host_addr_v4>>16)&0xFF)<<8) | ((sock->cap_info->host_addr_v4>>24)&0xFF); | |
| chk_sum += ((sock->cap_info->peer_addr_v4&0xFF)<<8) | ((sock->cap_info->peer_addr_v4>>8)&0xFF); | |
| chk_sum += (((sock->cap_info->peer_addr_v4>>16)&0xFF)<<8) | ((sock->cap_info->peer_addr_v4>>24)&0xFF); | |
| if (chk_sum>0xFFFF) { | |
| u32 high = chk_sum>0xFFFF; | |
| chk_sum += high; | |
| } | |
| gf_bs_write_u16(nf->cap_bs, ~chk_sum); | |
| gf_bs_write_u32_le(nf->cap_bs, sock->cap_info->host_addr_v4); | |
| gf_bs_write_u32_le(nf->cap_bs, sock->cap_info->peer_addr_v4); | |
| } | |
| //UDP | |
| if (protocol_type==17) { | |
| gf_bs_write_u16(nf->cap_bs, sock->cap_info->host_port); | |
| gf_bs_write_u16(nf->cap_bs, sock->cap_info->peer_port); | |
| gf_bs_write_u16(nf->cap_bs, 8+length); | |
| //skip checksum | |
| gf_bs_skip_bytes(nf->cap_bs, 2); | |
| } | |
| //TCP | |
| else if (protocol_type==6) { | |
| gf_bs_write_u16(nf->cap_bs, sock->cap_info->host_port); | |
| gf_bs_write_u16(nf->cap_bs, sock->cap_info->peer_port); | |
| /*sn = */gf_bs_write_u32(nf->cap_bs, 0); | |
| /*ack = */gf_bs_write_u32(nf->cap_bs, 0); | |
| gf_bs_write_int(nf->cap_bs, 5, 4); | |
| gf_bs_write_int(nf->cap_bs, 0, 4); | |
| gf_bs_skip_bytes(nf->cap_bs, 7); | |
| } | |
| gf_bs_write_data(nf->cap_bs, buffer, length); | |
| if (is_ng) { | |
| if (ng_trail) gf_bs_skip_bytes(nf->cap_bs, ng_trail); | |
| gf_bs_write_u32(nf->cap_bs, ng_size); | |
| } | |
| if (written) *written = length; | |
| return GF_OK; | |
| } | |
| static GF_Err gf_netcap_send_gpac(GF_NetcapFilter *nf, GF_Socket *sock, const u8 *buffer, u32 length, u32 *written) | |
| { | |
| u64 now = gf_sys_clock_high_res(); | |
| if (!sock->cap_info->host_port) return GF_BAD_PARAM; | |
| if (!nf->init_time) nf->init_time = now; | |
| now -= nf->init_time; | |
| //setup flags and size flags | |
| u32 size = 1+8+2+length; | |
| u8 flags=0; | |
| if (sock->flags & GF_SOCK_IS_MULTICAST) flags |= NETCAP_IS_MCAST; | |
| if (sock->flags & GF_SOCK_IS_TCP) flags |= NETCAP_IS_TCP; | |
| if (sock->flags & GF_SOCK_HAS_PEER) flags |= NETCAP_HAS_PEER; | |
| if (sock->flags & GF_SOCK_IS_IPV6) { | |
| size += 16; | |
| if (sock->flags & GF_SOCK_HAS_PEER) { | |
| size += 18; | |
| } | |
| flags |= NETCAP_IS_IPV6; | |
| } else { | |
| size += 4; | |
| if (sock->flags & GF_SOCK_HAS_PEER) { | |
| size += 6; | |
| } | |
| } | |
| gf_bs_write_u32(nf->cap_bs, size); | |
| gf_bs_write_u8(nf->cap_bs, flags); | |
| gf_bs_write_u64(nf->cap_bs, now); | |
| if (sock->flags & GF_SOCK_IS_IPV6) { | |
| gf_bs_write_data(nf->cap_bs, sock->cap_info->host_addr_v6, 16); | |
| if (sock->flags & GF_SOCK_HAS_PEER) | |
| gf_bs_write_data(nf->cap_bs, sock->cap_info->peer_addr_v6, 16); | |
| } else { | |
| gf_bs_write_u32(nf->cap_bs, sock->cap_info->host_addr_v4); | |
| if (sock->flags & GF_SOCK_HAS_PEER) | |
| gf_bs_write_u32(nf->cap_bs, sock->cap_info->peer_addr_v4); | |
| } | |
| gf_bs_write_u16(nf->cap_bs, sock->cap_info->host_port); | |
| if (sock->flags & GF_SOCK_HAS_PEER) | |
| gf_bs_write_u16(nf->cap_bs, sock->cap_info->peer_port); | |
| gf_bs_write_data(nf->cap_bs, buffer, length); | |
| if (written) *written = length; | |
| return GF_OK; | |
| } | |
| static GF_Err gf_netcap_send(GF_Socket *sock, const u8 *buffer, u32 length, u32 *written) | |
| { | |
| GF_NetcapFilter *nf = sock->cap_info->nf; | |
| if (nf->cap_mode==NETCAP_PCAP) { | |
| return gf_netcap_send_pcap(nf, sock, buffer, length, written, GF_FALSE); | |
| } else if (nf->cap_mode==NETCAP_PCAPNG) { | |
| return gf_netcap_send_pcap(nf, sock, buffer, length, written, GF_TRUE); | |
| } else { | |
| return gf_netcap_send_gpac(nf, sock, buffer, length, written); | |
| } | |
| } | |
| #endif //GPAC_DISABLE_NETCAP | |
| Bool gf_sk_has_nrt_netcap(GF_Socket *sk) | |
| { | |
| if (sk && sk->cap_info && !sk->cap_info->nf->rt) return GF_TRUE; | |
| return GF_FALSE; | |
| } | |
| GF_EXPORT | |
| GF_Socket *gf_sk_new_ex(u32 SocketType, const char *netcap_id) | |
| { | |
| GF_Socket *tmp; | |
| /*init WinSock*/ | |
| #ifdef WIN32 | |
| WSADATA Data; | |
| if (!wsa_init && (WSAStartup(0x0202, &Data)!=0) ) return NULL; | |
| #endif | |
| switch (SocketType) { | |
| case GF_SOCK_TYPE_UDP: | |
| case GF_SOCK_TYPE_TCP: | |
| #ifdef GPAC_HAS_SOCK_UN | |
| case GF_SOCK_TYPE_UDP_UN: | |
| case GF_SOCK_TYPE_TCP_UN: | |
| #endif | |
| break; | |
| default: | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[Socket] unsupported socket type %d\n", SocketType)); | |
| return NULL; | |
| } | |
| GF_SAFEALLOC(tmp, GF_Socket); | |
| if (!tmp) return NULL; | |
| if (SocketType == GF_SOCK_TYPE_TCP) tmp->flags |= GF_SOCK_IS_TCP; | |
| #ifdef GPAC_HAS_SOCK_UN | |
| else if (SocketType == GF_SOCK_TYPE_TCP_UN) tmp->flags |= GF_SOCK_IS_TCP | GF_SOCK_IS_UN; | |
| else if (SocketType == GF_SOCK_TYPE_UDP_UN) tmp->flags |= GF_SOCK_IS_UN; | |
| #endif | |
| #ifdef GPAC_HAS_IPV6 | |
| memset(&tmp->dest_addr, 0, sizeof(struct sockaddr_storage)); | |
| #else | |
| memset(&tmp->dest_addr, 0, sizeof(struct sockaddr_in)); | |
| tmp->dest_addr_len = sizeof(struct sockaddr); | |
| #endif | |
| tmp->usec_wait = 500; | |
| #ifdef WIN32 | |
| wsa_init ++; | |
| #endif | |
| tmp->socket = INVALID_SOCKET; | |
| #ifndef GPAC_DISABLE_NETCAP | |
| GF_NetcapFilter *nf = gf_net_filter_get(netcap_id); | |
| if (nf && (nf->cap_bs || nf->nb_rules)) { | |
| GF_SAFEALLOC(tmp->cap_info, NetCapInfo); | |
| if (!tmp->cap_info) { | |
| gf_free(tmp); | |
| return NULL; | |
| } | |
| if (nf->read_socks) | |
| gf_list_add(nf->read_socks, tmp); | |
| tmp->cap_info->nf = nf; | |
| } | |
| #endif | |
| return tmp; | |
| } | |
| GF_EXPORT | |
| GF_Socket *gf_sk_new(u32 SocketType) | |
| { | |
| return gf_sk_new_ex(SocketType, NULL); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sk_set_buffer_size(GF_Socket *sock, Bool SendBuffer, u32 NewSize) | |
| { | |
| u32 nsize=0, psize; | |
| s32 res; | |
| if (!sock || SOCKET_INVALID(sock->socket)) return GF_BAD_PARAM; | |
| if (SendBuffer) { | |
| res = setsockopt(sock->socket, SOL_SOCKET, SO_SNDBUF, (char *) &NewSize, sizeof(u32) ); | |
| } else { | |
| res = setsockopt(sock->socket, SOL_SOCKET, SO_RCVBUF, (char *) &NewSize, sizeof(u32) ); | |
| } | |
| if (res<0) { | |
| GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[Socket] Couldn't set socket %s buffer size to %d: %s (%d)\n", SendBuffer ? "send" : "receive", NewSize, gf_errno_str(LASTSOCKERROR), LASTSOCKERROR)); | |
| } else { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[Socket] Set socket %s buffer size to %d\n", SendBuffer ? "send" : "receive", NewSize)); | |
| } | |
| psize = sizeof(u32); | |
| if (SendBuffer) { | |
| res = getsockopt(sock->socket, SOL_SOCKET, SO_SNDBUF, (char *) &nsize, &psize ); | |
| } else { | |
| res = getsockopt(sock->socket, SOL_SOCKET, SO_RCVBUF, (char *) &nsize, &psize ); | |
| } | |
| if ((res>=0) && (nsize!=NewSize)) { | |
| GF_LOG(nsize <= NewSize ? GF_LOG_WARNING : GF_LOG_DEBUG, GF_LOG_NETWORK, ("[Socket] Asked to set socket %s buffer size to %d but system used %d\n", SendBuffer ? "send" : "receive", NewSize, nsize)); | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sk_set_block_mode(GF_Socket *sock, Bool NonBlockingOn) | |
| { | |
| s32 res; | |
| #ifdef WIN32 | |
| long val = NonBlockingOn; | |
| if (!SOCKET_INVALID(sock->socket)) { | |
| res = ioctlsocket(sock->socket, FIONBIO, &val); | |
| if (res) return GF_SERVICE_ERROR; | |
| } | |
| #else | |
| if (!SOCKET_INVALID(sock->socket)) { | |
| s32 flags = fcntl(sock->socket, F_GETFL, 0); | |
| if (NonBlockingOn) | |
| flags |= O_NONBLOCK; | |
| else | |
| flags &= ~O_NONBLOCK; | |
| res = fcntl(sock->socket, F_SETFL, flags); | |
| if (res) return GF_SERVICE_ERROR; | |
| } | |
| #endif | |
| if (NonBlockingOn) { | |
| sock->flags |= GF_SOCK_NON_BLOCKING; | |
| } else { | |
| sock->flags &= ~GF_SOCK_NON_BLOCKING; | |
| } | |
| return GF_OK; | |
| } | |
| #include <assert.h> | |
| #ifndef GPAC_DISABLE_NETCAP | |
| #define sock_close(_s) \ | |
| closesocket(_s->socket);\ | |
| _s->socket = INVALID_SOCKET;\ | |
| if (_s->cap_info) _s->cap_info->host_port = 0; \ | |
| #else | |
| #define sock_close(_s) \ | |
| closesocket(_s->socket);\ | |
| _s->socket = INVALID_SOCKET; | |
| #endif | |
| static void gf_sk_free(GF_Socket *sock) | |
| { | |
| gf_assert( sock ); | |
| if (SOCKET_INVALID(sock->socket)) return; | |
| /*leave multicast*/ | |
| if (sock->flags & GF_SOCK_IS_MULTICAST) { | |
| struct ip_mreq mreq; | |
| #ifdef GPAC_HAS_IPV6 | |
| struct sockaddr *addr = (struct sockaddr *)&sock->dest_addr; | |
| if (addr->sa_family==AF_INET6) { | |
| struct ipv6_mreq mreq6; | |
| memcpy(&mreq6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr)); | |
| mreq6.ipv6mr_interface= 0; | |
| setsockopt(sock->socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *) &mreq6, sizeof(mreq6)); | |
| } else { | |
| mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr; | |
| mreq.imr_interface.s_addr = INADDR_ANY; | |
| setsockopt(sock->socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *) &mreq, sizeof(mreq)); | |
| } | |
| #else | |
| mreq.imr_multiaddr.s_addr = sock->dest_addr.sin_addr.s_addr; | |
| mreq.imr_interface.s_addr = INADDR_ANY; | |
| setsockopt(sock->socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *) &mreq, sizeof(mreq)); | |
| #endif | |
| } | |
| sock_close(sock); | |
| } | |
| GF_EXPORT | |
| void gf_sk_del(GF_Socket *sock) | |
| { | |
| gf_assert( sock ); | |
| gf_sk_free(sock); | |
| #ifdef WIN32 | |
| wsa_init --; | |
| if (!wsa_init) WSACleanup(); | |
| #endif | |
| #ifndef GPAC_DISABLE_NETCAP | |
| if (sock->cap_info) { | |
| if (sock->cap_info->nf->read_socks) | |
| gf_list_del_item(sock->cap_info->nf->read_socks, sock); | |
| gf_free(sock->cap_info); | |
| } | |
| #endif | |
| gf_free(sock); | |
| } | |
| GF_EXPORT | |
| void gf_sk_reset(GF_Socket *sock) | |
| { | |
| if (sock) { | |
| u32 clear=0; | |
| setsockopt(sock->socket, SOL_SOCKET, SO_ERROR, (char *) &clear, sizeof(u32) ); | |
| } | |
| } | |
| GF_EXPORT | |
| s32 gf_sk_get_handle(GF_Socket *sock) | |
| { | |
| return (s32) sock->socket; | |
| } | |
| GF_EXPORT | |
| void gf_sk_set_usec_wait(GF_Socket *sock, u32 usec_wait) | |
| { | |
| if (!sock) return; | |
| sock->usec_wait = (usec_wait>=1000000) ? 500 : usec_wait; | |
| } | |
| #ifdef GPAC_STATIC_BIN | |
| struct hostent *gf_gethostbyname(const char *PeerName) | |
| { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("Static GPAC build has no DNS support, cannot resolve host %s !\n", PeerName)); | |
| return NULL; | |
| } | |
| #else | |
| #define gf_gethostbyname gethostbyname | |
| #endif | |
| static u32 inet_addr_from_name(const char *local_interface); | |
| //connects a socket to a remote peer on a given port | |
| GF_Err gf_sk_connect_ex(GF_Socket *sock, const char *PeerName, u16 PortNumber, const char *ifce_ip_or_name, Bool use_udp_connect) | |
| { | |
| s32 ret; | |
| #ifdef GPAC_HAS_IPV6 | |
| u32 type; | |
| struct addrinfo *res, *aip, *lip; | |
| #else | |
| struct hostent *Host = NULL; | |
| #endif | |
| if (sock->flags & GF_SOCK_IS_UN) { | |
| #ifdef GPAC_HAS_SOCK_UN | |
| struct sockaddr_un server_add; | |
| if (SOCKET_INVALID(sock->socket)) { | |
| sock->socket = socket(AF_UNIX, (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM, 0); | |
| if (sock->flags & GF_SOCK_NON_BLOCKING) | |
| gf_sk_set_block_mode(sock, GF_TRUE); | |
| } | |
| server_add.sun_family = AF_UNIX; | |
| gf_strcpy(server_add.sun_path, PeerName); | |
| server_add.sun_path[sizeof(server_add.sun_path)-1]=0; | |
| if (connect(sock->socket, (struct sockaddr *)&server_add, sizeof(struct sockaddr_un)) < 0) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[Socket] Failed to connect unix domain socket to %s\n", PeerName)); | |
| return GF_IP_CONNECTION_FAILURE; | |
| } | |
| return GF_OK; | |
| #else | |
| return GF_NOT_SUPPORTED; | |
| #endif | |
| } | |
| #ifdef GPAC_HAS_IPV6 | |
| type = (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM; | |
| GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[Sock_IPV6] Solving %s address\n", PeerName)); | |
| res = gf_sk_get_ipv6_addr(PeerName, PortNumber, AF_UNSPEC, AI_PASSIVE, type); | |
| if (!res) return GF_IP_CONNECTION_FAILURE; | |
| GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[Sock_IPV6] Host %s found\n", PeerName)); | |
| lip = NULL; | |
| if (ifce_ip_or_name) { | |
| lip = gf_sk_get_ifce_ipv6_addr(ifce_ip_or_name, PortNumber, AF_UNSPEC, AI_PASSIVE, type); | |
| if (!lip) return GF_IP_CONNECTION_FAILURE; | |
| } | |
| /*for all interfaces*/ | |
| for (aip=res; aip!=NULL; aip=aip->ai_next) { | |
| if (type != (u32) aip->ai_socktype) continue; | |
| #ifndef GPAC_DISABLE_NETCAP | |
| if (sock->cap_info) { | |
| sock->cap_info->host_port = PortNumber; | |
| if (lip) { | |
| if (lip->ai_family==PF_INET) { | |
| sock->cap_info->host_addr_v4 = inet_addr_from_name(ifce_ip_or_name); | |
| } else { | |
| memcpy(sock->cap_info->host_addr_v6, (u8*) &((struct sockaddr_in6 *)lip->ai_addr)->sin6_addr, sizeof(bin128)); | |
| } | |
| } | |
| if (aip->ai_family==AF_INET) { | |
| sock->cap_info->peer_addr_v4 = inet_addr_from_name(PeerName); | |
| sock->cap_info->peer_addr_v4 = ((struct sockaddr_in *)aip->ai_addr)->sin_addr.s_addr; | |
| } else { | |
| memcpy(sock->cap_info->peer_addr_v6, (u8*) &((struct sockaddr_in6 *)aip->ai_addr)->sin6_addr, sizeof(bin128)); | |
| } | |
| //play/record, don't create socket | |
| if (sock->cap_info->nf->cap_mode) { | |
| if (sock->cap_info->nf->read_socks==NULL) { | |
| sock->cap_info->peer_port = first_tcp_port; | |
| first_tcp_port++; | |
| } | |
| sock->flags |= GF_SOCK_HAS_PEER; | |
| freeaddrinfo(res); | |
| if (lip) freeaddrinfo(lip); | |
| return GF_OK; | |
| } | |
| } | |
| #endif | |
| if (SOCKET_INVALID(sock->socket)) { | |
| sock->socket = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); | |
| if (SOCKET_INVALID(sock->socket)) { | |
| continue; | |
| } | |
| if (sock->flags & GF_SOCK_IS_TCP) { | |
| if (sock->flags & GF_SOCK_NON_BLOCKING) | |
| gf_sk_set_block_mode(sock, GF_TRUE); | |
| } | |
| if (aip->ai_family==PF_INET6) sock->flags |= GF_SOCK_IS_IPV6; | |
| else sock->flags &= ~GF_SOCK_IS_IPV6; | |
| if (lip) { | |
| ret = bind(sock->socket, lip->ai_addr, (int) lip->ai_addrlen); | |
| if (ret == SOCKET_ERROR) { | |
| sock_close(sock); | |
| continue; | |
| } | |
| } | |
| } | |
| #if defined(WIN32) || defined(_WIN32_WCE) | |
| //on winsock we must check writability between two connects for non-blocking sockets | |
| if ((sock->flags & GF_SOCK_IS_TCP) && (sock->flags & GF_SOCK_HAS_CONNECT)) { | |
| if (gf_sk_select(sock, GF_SK_SELECT_WRITE) == GF_IP_NETWORK_EMPTY) { | |
| GF_Err e = gf_sk_probe(sock); | |
| if (e && (e != GF_IP_NETWORK_EMPTY)) return e; | |
| return GF_IP_NETWORK_EMPTY; | |
| } | |
| } | |
| #endif | |
| if (sock->flags & GF_SOCK_IS_TCP) { | |
| GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[Sock_IPV6] Connecting to %s:%d\n", PeerName, PortNumber)); | |
| } else if (!use_udp_connect) { | |
| goto conn_ok; | |
| } | |
| ret = connect(sock->socket, aip->ai_addr, (int) aip->ai_addrlen); | |
| if (ret == SOCKET_ERROR) { | |
| int err = LASTSOCKERROR; | |
| if (sock->flags & GF_SOCK_NON_BLOCKING) { | |
| switch (err) { | |
| case EINPROGRESS: | |
| #if defined(WIN32) || defined(_WIN32_WCE) | |
| case WSAEWOULDBLOCK: | |
| #endif | |
| freeaddrinfo(res); | |
| if (lip) freeaddrinfo(lip); | |
| //remember we issued a first connect | |
| sock->flags |= GF_SOCK_HAS_CONNECT; | |
| return GF_IP_NETWORK_EMPTY; | |
| case EISCONN: | |
| case EALREADY: | |
| #if defined(WIN32) || defined(_WIN32_WCE) | |
| case WSAEISCONN: | |
| #endif | |
| if (sock->flags & GF_SOCK_IS_TCP) { | |
| if (gf_sk_select(sock, GF_SK_SELECT_WRITE) == GF_OK) | |
| goto conn_ok; | |
| } | |
| freeaddrinfo(res); | |
| if (lip) freeaddrinfo(lip); | |
| return GF_IP_NETWORK_EMPTY; | |
| } | |
| } | |
| sock_close(sock); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[Sock_IPV6] Failed to connect to host %s: %s (%d) - retrying with next host address\n", PeerName, gf_errno_str(err), err )); | |
| continue; | |
| } | |
| conn_ok: | |
| if (sock->flags & GF_SOCK_IS_TCP) { | |
| GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[Sock_IPV6] Connected to %s:%d\n", PeerName, PortNumber)); | |
| } else { | |
| //udp+connect, do not use HAS_PEER | |
| sock->flags &= ~GF_SOCK_HAS_PEER; | |
| } | |
| sock->flags &= ~GF_SOCK_HAS_CONNECT; | |
| #ifdef SO_NOSIGPIPE | |
| int value = 1; | |
| setsockopt(sock->socket, SOL_SOCKET, SO_NOSIGPIPE, SSO_CAST &value, sizeof(value)); | |
| #endif | |
| memcpy(&sock->dest_addr, aip->ai_addr, aip->ai_addrlen); | |
| sock->dest_addr_len = (u32) aip->ai_addrlen; | |
| freeaddrinfo(res); | |
| if (lip) freeaddrinfo(lip); | |
| return GF_OK; | |
| } | |
| freeaddrinfo(res); | |
| if (lip) freeaddrinfo(lip); | |
| return GF_IP_CONNECTION_FAILURE; | |
| #else | |
| #ifndef GPAC_DISABLE_NETCAP | |
| if (sock->cap_info) { | |
| sock->cap_info->host_port = PortNumber; | |
| if (ifce_ip_or_name) | |
| sock->cap_info->host_addr_v4 = inet_addr_from_name(ifce_ip_or_name); | |
| sock->cap_info->peer_addr_v4 = inet_addr_from_name(PeerName); | |
| if (sock->cap_info->nf->read_socks==NULL) { | |
| sock->cap_info->peer_port = first_tcp_port; | |
| first_tcp_port++; | |
| } | |
| //play/record, don't create socket | |
| if (sock->cap_info->nf->cap_mode) { | |
| sock->flags |= GF_SOCK_HAS_PEER; | |
| return GF_OK; | |
| } | |
| } | |
| #endif | |
| if (SOCKET_INVALID(sock->socket)) { | |
| sock->socket = socket(AF_INET, (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM, 0); | |
| if (sock->flags & GF_SOCK_NON_BLOCKING) | |
| gf_sk_set_block_mode(sock, GF_TRUE); | |
| } | |
| /*setup the address*/ | |
| sock->dest_addr.sin_family = AF_INET; | |
| sock->dest_addr.sin_port = htons(PortNumber); | |
| /*get the server IP*/ | |
| sock->dest_addr.sin_addr.s_addr = inet_addr(PeerName); | |
| if (sock->dest_addr.sin_addr.s_addr==INADDR_NONE) { | |
| GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[Sock_IPV4] Solving %s address\n", PeerName)); | |
| Host = gf_gethostbyname(PeerName); | |
| if (Host == NULL) { | |
| GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[Sock_IPV4] Failed to retrieve host %s address: %s\n", PeerName, gf_errno_str(LASTSOCKERROR) )); | |
| switch (LASTSOCKERROR) { | |
| #ifndef __SYMBIAN32__ | |
| case ENETDOWN: | |
| return GF_IP_NETWORK_FAILURE; | |
| //case ENOHOST: return GF_IP_ADDRESS_NOT_FOUND; | |
| #endif | |
| default: | |
| return GF_IP_NETWORK_FAILURE; | |
| } | |
| } | |
| GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[Sock_IPV4] Host %s found\n", PeerName)); | |
| memcpy(&sock->dest_addr.sin_addr, Host->h_addr_list[0], sizeof(u8)*Host->h_length); | |
| } | |
| if (ifce_ip_or_name) { | |
| GF_Err e = gf_sk_bind(sock, ifce_ip_or_name, PortNumber, PeerName, PortNumber, GF_SOCK_REUSE_PORT); | |
| if (e) return e; | |
| } | |
| #if defined(WIN32) || defined(_WIN32_WCE) | |
| //on winsock we must check writability between two connects for non-blocking sockets | |
| if ((sock->flags & GF_SOCK_IS_TCP) && (sock->flags & GF_SOCK_HAS_CONNECT)) { | |
| if (gf_sk_select(sock, GF_SK_SELECT_WRITE) == GF_IP_NETWORK_EMPTY) | |
| return GF_IP_NETWORK_EMPTY; | |
| } | |
| #endif | |
| if (sock->flags & GF_SOCK_IS_TCP) { | |
| GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[Sock_IPV4] Connecting to %s:%d\n", PeerName, PortNumber)); | |
| } else if (!use_udp_connect) { | |
| return GF_OK; | |
| } | |
| ret = connect(sock->socket, (struct sockaddr *) &sock->dest_addr, sizeof(struct sockaddr)); | |
| if (ret == SOCKET_ERROR) { | |
| u32 res = LASTSOCKERROR; | |
| if (sock->flags & GF_SOCK_NON_BLOCKING) { | |
| switch (res) { | |
| case EINPROGRESS: | |
| #if defined(WIN32) || defined(_WIN32_WCE) | |
| case WSAEWOULDBLOCK: | |
| #endif | |
| //remember we issued a first connect | |
| sock->flags |= GF_SOCK_HAS_CONNECT; | |
| return GF_IP_NETWORK_EMPTY; | |
| case EISCONN: | |
| case EALREADY: | |
| #if defined(WIN32) || defined(_WIN32_WCE) | |
| case WSAEISCONN: | |
| #endif | |
| if (sock->flags & GF_SOCK_IS_TCP) { | |
| if (gf_sk_select(sock, GF_SK_SELECT_WRITE) == GF_OK) | |
| return GF_OK; | |
| } | |
| return GF_IP_NETWORK_EMPTY; | |
| } | |
| } | |
| sock->flags &= ~GF_SOCK_HAS_CONNECT; | |
| switch (res) { | |
| case EAGAIN: | |
| case EINTR: | |
| #if defined(WIN32) || defined(_WIN32_WCE) | |
| case WSAEWOULDBLOCK: | |
| #endif | |
| return GF_IP_NETWORK_EMPTY; | |
| #ifdef WIN32 | |
| case WSAEINVAL: | |
| if (sock->flags & GF_SOCK_NON_BLOCKING) | |
| return GF_IP_NETWORK_EMPTY; | |
| #endif | |
| case EISCONN: | |
| return GF_OK; | |
| default: | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[Sock_IPV4] Couldn't connect socket: %s\n", gf_errno_str(res))); | |
| return GF_IP_CONNECTION_FAILURE; | |
| } | |
| } | |
| sock->flags &= ~GF_SOCK_HAS_CONNECT; | |
| if (sock->flags & GF_SOCK_IS_TCP) { | |
| GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[Sock_IPV4] Connected to %s:%d\n", PeerName, PortNumber)); | |
| } | |
| #endif | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sk_connect(GF_Socket *sock, const char *PeerName, u16 PortNumber, const char *ifce_ip_or_name) | |
| { | |
| return gf_sk_connect_ex(sock, PeerName, PortNumber, ifce_ip_or_name, GF_FALSE); | |
| } | |
| //binds the given socket to the specified port. If ReUse is true | |
| //this will enable reuse of ports on a single machine | |
| GF_EXPORT | |
| GF_Err gf_sk_bind_ex(GF_Socket *sock, const char *ifce_ip_or_name, u16 port, const char *peer_name, u16 peer_port, u32 options, | |
| u8 **dst_sock_addr, u32 *dst_sock_addr_len, u8 **src_sock_addr, u32 *src_sock_addr_len) | |
| { | |
| #ifdef GPAC_HAS_IPV6 | |
| struct addrinfo *res, *aip; | |
| int af; | |
| u32 type; | |
| #else | |
| u32 ip_add; | |
| size_t addrlen; | |
| struct sockaddr_in LocalAdd; | |
| struct hostent *Host = NULL; | |
| #endif | |
| s32 ret = 0; | |
| s32 optval; | |
| if (dst_sock_addr) { | |
| *dst_sock_addr = NULL; | |
| *dst_sock_addr_len = 0; | |
| } | |
| if (src_sock_addr) { | |
| *src_sock_addr = NULL; | |
| *src_sock_addr_len = 0; | |
| } | |
| //socket must not be created | |
| if (!sock || !SOCKET_INVALID(sock->socket)) return GF_BAD_PARAM; | |
| if (ifce_ip_or_name && !strcmp(ifce_ip_or_name, "127.0.0.1")) | |
| ifce_ip_or_name = NULL; | |
| if (sock->flags & GF_SOCK_IS_UN) { | |
| #ifdef GPAC_HAS_SOCK_UN | |
| struct sockaddr_un server_un; | |
| sock->socket = socket(AF_UNIX, (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM, 0); | |
| if (sock->flags & GF_SOCK_NON_BLOCKING) | |
| gf_sk_set_block_mode(sock, GF_TRUE); | |
| server_un.sun_family = AF_UNIX; | |
| gf_strcpy(server_un.sun_path, peer_name); | |
| ret = bind(sock->socket, (struct sockaddr *) &server_un, (int) sizeof(struct sockaddr_un)); | |
| if (ret == SOCKET_ERROR) { | |
| if (LASTSOCKERROR == EADDRINUSE) { | |
| return gf_sk_connect(sock, peer_name, peer_port, NULL); | |
| } | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[socket] cannot bind socket: %s\n", gf_errno_str(LASTSOCKERROR) )); | |
| return GF_IP_CONNECTION_FAILURE; | |
| } | |
| GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[socket] socket bound to unix domain %s\n", peer_name)); | |
| return GF_OK; | |
| #else | |
| return GF_NOT_SUPPORTED; | |
| #endif | |
| } | |
| #ifndef WIN32 | |
| if (!ifce_ip_or_name && (!peer_name || !strcmp(peer_name,"localhost"))) { | |
| peer_name="127.0.0.1"; | |
| } | |
| #endif | |
| #ifdef GPAC_HAS_IPV6 | |
| type = (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM; | |
| af = (options & GF_SOCK_FORCE_IPV6) ? PF_INET6 : PF_UNSPEC; | |
| if (!gf_net_has_ipv6()) af = PF_INET; | |
| /*probe way to peer: is it V4 or V6? */ | |
| if (peer_name && peer_port) { | |
| res = gf_sk_get_ipv6_addr(peer_name, peer_port, af, AI_PASSIVE, type); | |
| if (!res) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[Socket] Cannot get IPV6 host name for %s:%d\n", peer_name, peer_port)); | |
| return GF_IP_ADDRESS_NOT_FOUND; | |
| } | |
| #ifdef WIN32 | |
| /*win32 has troubles redirecting IPV4 datagrams to IPV6 sockets, so override | |
| local family type to avoid IPV4(S)->IPV6(C) UDP*/ | |
| af = res->ai_family; | |
| #endif | |
| memcpy(&sock->dest_addr, res->ai_addr, res->ai_addrlen); | |
| sock->dest_addr_len = (u32) res->ai_addrlen; | |
| freeaddrinfo(res); | |
| if (dst_sock_addr) { | |
| *dst_sock_addr = gf_malloc(sizeof(u8) * sock->dest_addr_len); | |
| memcpy(*dst_sock_addr, &sock->dest_addr, sock->dest_addr_len); | |
| *dst_sock_addr_len = sock->dest_addr_len; | |
| } | |
| } | |
| res = gf_sk_get_ifce_ipv6_addr(ifce_ip_or_name, port, af, AI_PASSIVE, type); | |
| if (!res) { | |
| if (dst_sock_addr && *dst_sock_addr) { | |
| gf_free(*dst_sock_addr); | |
| *dst_sock_addr = NULL; | |
| } | |
| return GF_IP_ADDRESS_NOT_FOUND; | |
| } | |
| /*for all interfaces*/ | |
| for (aip=res; aip!=NULL; aip=aip->ai_next) { | |
| if (type != (u32) aip->ai_socktype) continue; | |
| if (aip->ai_next && (aip->ai_next->ai_family==PF_INET) && !gf_net_is_ipv6(peer_name)) continue; | |
| #ifndef GPAC_DISABLE_NETCAP | |
| if (sock->cap_info) { | |
| sock->cap_info->host_port = port ? port : peer_port; | |
| if (aip->ai_family==PF_INET) { | |
| sock->cap_info->host_addr_v4 = inet_addr_from_name(peer_name ? peer_name : ifce_ip_or_name); | |
| } else { | |
| memcpy(sock->cap_info->host_addr_v6, (u8*) &((struct sockaddr_in6 *)aip->ai_addr)->sin6_addr, sizeof(bin128)); | |
| } | |
| if (peer_name && peer_port) { | |
| if (sock->dest_addr.ss_family==AF_INET) { | |
| struct sockaddr_in *r_add = (struct sockaddr_in *) &sock->dest_addr; | |
| sock->cap_info->peer_port = ntohs(r_add->sin_port); | |
| sock->cap_info->peer_addr_v4 = r_add->sin_addr.s_addr; | |
| } else { | |
| struct sockaddr_in6 *r_add = (struct sockaddr_in6 *) &sock->dest_addr; | |
| sock->cap_info->peer_port = ntohs(r_add->sin6_port); | |
| memcpy(sock->cap_info->peer_addr_v6, &(r_add->sin6_addr), sizeof(bin128)); | |
| } | |
| } | |
| //play/record, don't create socket | |
| if (sock->cap_info->nf->cap_mode) { | |
| if (aip->ai_family==PF_INET6) sock->flags |= GF_SOCK_IS_IPV6; | |
| else sock->flags &= ~GF_SOCK_IS_IPV6; | |
| freeaddrinfo(res); | |
| if (dst_sock_addr && *dst_sock_addr) { | |
| gf_free(*dst_sock_addr); | |
| *dst_sock_addr = NULL; | |
| } | |
| return GF_OK; | |
| } | |
| } | |
| #endif | |
| sock->socket = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); | |
| if (SOCKET_INVALID(sock->socket)) { | |
| continue; | |
| } | |
| if (options & GF_SOCK_REUSE_PORT) { | |
| optval = 1; | |
| setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, (const char *) &optval, sizeof(optval)); | |
| #ifdef SO_REUSEPORT | |
| optval = 1; | |
| setsockopt(sock->socket, SOL_SOCKET, SO_REUSEPORT, SSO_CAST &optval, sizeof(optval)); | |
| #endif | |
| } | |
| if (sock->flags & GF_SOCK_NON_BLOCKING) | |
| gf_sk_set_block_mode(sock, GF_TRUE); | |
| if (peer_name && peer_port) { | |
| sock->flags |= GF_SOCK_HAS_PEER; | |
| } | |
| #if defined(GPAC_CONFIG_LINUX) || defined(GPAC_CONFIG_DARWIN) | |
| //we use implicit bind (assign on first sendto) - not doing so makes poll/select fail in readers lanched after the sender | |
| if (peer_name && !strcmp(peer_name, "127.0.0.1") && (options & GF_SOCK_IS_SENDER)) { | |
| } else | |
| #endif | |
| { | |
| ret = bind(sock->socket, aip->ai_addr, (int) aip->ai_addrlen); | |
| if (ret == SOCKET_ERROR) { | |
| if (dst_sock_addr && *dst_sock_addr) { | |
| gf_free(*dst_sock_addr); | |
| *dst_sock_addr = NULL; | |
| } | |
| sock_close(sock); | |
| if (!(options & GF_SOCK_REUSE_PORT) && (LASTSOCKERROR == EADDRINUSE)) { | |
| return GF_IP_CONNECTION_FAILURE; | |
| } | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[socket] bind failed: %s\n", gf_errno_str(LASTSOCKERROR) )); | |
| continue; | |
| } | |
| } | |
| if (aip->ai_family==PF_INET6) sock->flags |= GF_SOCK_IS_IPV6; | |
| else sock->flags &= ~GF_SOCK_IS_IPV6; | |
| if (src_sock_addr) { | |
| *src_sock_addr = gf_malloc(sizeof(u8) * res->ai_addrlen); | |
| memcpy(*src_sock_addr, res->ai_addr, res->ai_addrlen); | |
| *src_sock_addr_len = (u32) res->ai_addrlen; | |
| } | |
| freeaddrinfo(res); | |
| return GF_OK; | |
| } | |
| freeaddrinfo(res); | |
| if (dst_sock_addr && *dst_sock_addr) { | |
| gf_free(*dst_sock_addr); | |
| *dst_sock_addr = NULL; | |
| } | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[Socket] Cannot bind to ifce %s port %d\n", ifce_ip_or_name ? ifce_ip_or_name : "any", port)); | |
| return GF_IP_NETWORK_FAILURE; | |
| #else | |
| #ifndef GPAC_DISABLE_NETCAP | |
| if (sock->cap_info) { | |
| sock->cap_info->host_port = port ? port : peer_port; | |
| sock->cap_info->host_addr_v4 = inet_addr_from_name(ifce_ip_or_name); | |
| if (peer_name && peer_port) { | |
| sock->cap_info->peer_port = peer_port; | |
| sock->cap_info->peer_addr_v4 = inet_addr_from_name(peer_name); | |
| sock->flags |= GF_SOCK_HAS_PEER; | |
| } | |
| //play/record, don't create socket | |
| if (sock->cap_info->nf->cap_mode) { | |
| return GF_OK; | |
| } | |
| } | |
| #endif | |
| sock->socket = socket(AF_INET, (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM, 0); | |
| if (sock->flags & GF_SOCK_NON_BLOCKING) | |
| gf_sk_set_block_mode(sock, GF_TRUE); | |
| sock->flags &= ~GF_SOCK_IS_IPV6; | |
| memset((void *) &LocalAdd, 0, sizeof(LocalAdd)); | |
| /*setup the address*/ | |
| ip_add = inet_addr_from_name(ifce_ip_or_name); | |
| if (ip_add==0xFFFFFFFF) return GF_IP_CONNECTION_FAILURE; | |
| if (peer_name && peer_port) { | |
| #ifdef WIN32 | |
| if ((inet_addr(peer_name)== ip_add) || !strcmp(peer_name, "127.0.0.1") ) { | |
| optval = 1; | |
| setsockopt(sock->socket, SOL_SOCKET, SO_USELOOPBACK, SSO_CAST &optval, sizeof(optval)); | |
| } | |
| #endif | |
| } | |
| LocalAdd.sin_family = AF_INET; | |
| LocalAdd.sin_port = htons(port); | |
| LocalAdd.sin_addr.s_addr = ip_add; | |
| addrlen = sizeof(struct sockaddr_in); | |
| if (src_sock_addr) { | |
| *src_sock_addr = gf_malloc(sizeof(u8) * addrlen); | |
| memcpy(*src_sock_addr, &LocalAdd, addrlen); | |
| *src_sock_addr_len = addrlen; | |
| } | |
| if (options & GF_SOCK_REUSE_PORT) { | |
| optval = 1; | |
| setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, SSO_CAST &optval, sizeof(optval)); | |
| #ifdef SO_REUSEPORT | |
| optval = 1; | |
| setsockopt(sock->socket, SOL_SOCKET, SO_REUSEPORT, SSO_CAST &optval, sizeof(optval)); | |
| #endif | |
| } | |
| /*bind the socket*/ | |
| #ifdef GPAC_CONFIG_LINUX | |
| //see above comment | |
| if (peer_name && !strcmp(peer_name, "127.0.0.1") && (options & GF_SOCK_IS_SENDER) ) { | |
| } else | |
| #endif | |
| { | |
| ret = bind(sock->socket, (struct sockaddr *) &LocalAdd, (int) addrlen); | |
| if (ret == SOCKET_ERROR) { | |
| if (!(options & GF_SOCK_REUSE_PORT) && (LASTSOCKERROR == EADDRINUSE)) { | |
| if (src_sock_addr && *src_sock_addr) { | |
| gf_free(*src_sock_addr); | |
| *src_sock_addr = NULL; | |
| } | |
| return GF_IP_CONNECTION_FAILURE; | |
| } | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[socket] cannot bind socket: %s\n", gf_errno_str(LASTSOCKERROR) )); | |
| ret = GF_IP_NETWORK_FAILURE; | |
| } | |
| } | |
| if (peer_name && peer_port) { | |
| sock->dest_addr.sin_port = htons(peer_port); | |
| sock->dest_addr.sin_family = AF_INET; | |
| sock->dest_addr.sin_addr.s_addr = inet_addr(peer_name); | |
| if (sock->dest_addr.sin_addr.s_addr == INADDR_NONE) { | |
| Host = gf_gethostbyname(peer_name); | |
| if (Host == NULL) ret = GF_IP_ADDRESS_NOT_FOUND; | |
| else memcpy((char *) &sock->dest_addr.sin_addr, Host->h_addr_list[0], sizeof(u32)); | |
| } | |
| sock->flags |= GF_SOCK_HAS_PEER; | |
| if (dst_sock_addr && !ret) { | |
| *dst_sock_addr = gf_malloc(sizeof(u8) * sock->dest_addr_len); | |
| memcpy(*dst_sock_addr, &sock->dest_addr, sock->dest_addr_len); | |
| *dst_sock_addr_len = sock->dest_addr_len; | |
| } | |
| } | |
| if (sock->flags & GF_SOCK_HAS_PEER) { | |
| GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[socket] socket bound to %08X - port %d - remote peer: %s:%d\n", ip_add, port, peer_name, peer_port)); | |
| } else { | |
| GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[socket] socket bound to %08X - port %d\n", ip_add, port)); | |
| } | |
| return ret; | |
| #endif | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sk_bind(GF_Socket *sock, const char *ifce_ip_or_name, u16 port, const char *peer_name, u16 peer_port, u32 options) | |
| { | |
| return gf_sk_bind_ex(sock, ifce_ip_or_name, port, peer_name, peer_port, options, NULL, NULL, NULL, NULL); | |
| } | |
| Bool gpac_use_poll=GF_TRUE; | |
| static GF_Err poll_select(GF_Socket *sock, GF_SockSelectMode mode, u32 usec, Bool force_select) | |
| { | |
| #ifndef __SYMBIAN32__ | |
| int ready; | |
| struct timeval timeout; | |
| #ifdef GPAC_HAS_POLL | |
| if (!force_select && gpac_use_poll) { | |
| struct pollfd pfd; | |
| pfd.fd = sock->socket; | |
| pfd.revents = 0; | |
| if (mode == GF_SK_SELECT_WRITE) { | |
| //ugly patch, poll(timeout=0) sometimes crash on arm64 linux... (M4, ubuntu24 vm) | |
| #if defined(GPAC_CONFIG_ARM) && defined(GPAC_CONFIG_LINUX) | |
| if (usec<1000) usec = 1000; | |
| #endif | |
| pfd.events = POLLOUT; | |
| } | |
| else if (mode == GF_SK_SELECT_READ) pfd.events = POLLIN; | |
| else pfd.events = POLLIN|POLLOUT; | |
| ready = poll(&pfd, 1, usec/1000); | |
| if (ready<0) { | |
| switch (LASTSOCKERROR) { | |
| case EAGAIN: | |
| case EINTR: | |
| return GF_IP_NETWORK_EMPTY; | |
| default: | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[socket] cannot poll: %s\n", gf_errno_str(LASTSOCKERROR) )); | |
| return GF_IP_NETWORK_FAILURE; | |
| } | |
| } | |
| if (!ready) | |
| return GF_IP_NETWORK_EMPTY; | |
| if ((mode == GF_SK_SELECT_WRITE) && !(pfd.revents & POLLOUT)) | |
| return GF_IP_NETWORK_EMPTY; | |
| if ((mode == GF_SK_SELECT_READ) && !(pfd.revents & POLLIN)) | |
| return GF_IP_NETWORK_EMPTY; | |
| return GF_OK; | |
| } | |
| #endif | |
| fd_set rgroup, wgroup; | |
| //can we write? | |
| FD_ZERO(&rgroup); | |
| FD_ZERO(&wgroup); | |
| if (mode != GF_SK_SELECT_WRITE) | |
| FD_SET(sock->socket, &rgroup); | |
| if (mode != GF_SK_SELECT_READ) | |
| FD_SET(sock->socket, &wgroup); | |
| timeout.tv_sec = 0; | |
| timeout.tv_usec = usec; | |
| ready = select((int) sock->socket+1, | |
| (mode != GF_SK_SELECT_WRITE) ? &rgroup : NULL, | |
| (mode != GF_SK_SELECT_READ) ? &wgroup : NULL, | |
| NULL, &timeout); | |
| if (ready == SOCKET_ERROR) { | |
| switch (LASTSOCKERROR) { | |
| case EAGAIN: | |
| case EINTR: | |
| return GF_IP_NETWORK_EMPTY; | |
| default: | |
| GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[socket] select failure: %s\n", gf_errno_str(LASTSOCKERROR))); | |
| return GF_IP_NETWORK_FAILURE; | |
| } | |
| } | |
| if (!ready) | |
| return GF_IP_NETWORK_EMPTY; | |
| if ((mode == GF_SK_SELECT_WRITE) && !FD_ISSET(sock->socket, &wgroup)) | |
| return GF_IP_NETWORK_EMPTY; | |
| if ((mode == GF_SK_SELECT_READ) && !FD_ISSET(sock->socket, &rgroup)) | |
| return GF_IP_NETWORK_EMPTY; | |
| //if mode is both and we are ready, let's go | |
| return GF_OK; | |
| #endif | |
| } | |
| //send length bytes of a buffer | |
| static GF_Err gf_sk_send_internal(GF_Socket *sock, const u8 *buffer, u32 length, const u8 *address, u32 address_len, u32 *written) | |
| { | |
| u32 count; | |
| s32 res; | |
| if (written) *written = 0; | |
| #ifndef GPAC_DISABLE_NETCAP | |
| if (netcap_filter_pck(sock, length, GF_TRUE)) { | |
| if (written) *written = length; | |
| return GF_OK; | |
| } | |
| if (sock->cap_info) { | |
| if (sock->cap_info->patch_offset) { | |
| ((u8*)buffer)[sock->cap_info->patch_offset-1] = sock->cap_info->patch_val; | |
| sock->cap_info->patch_offset = 0; | |
| } | |
| if (sock->cap_info->nf->read_socks==NULL) | |
| return gf_netcap_send(sock, buffer, length, written); | |
| if (sock->cap_info->nf->read_socks) | |
| return GF_OK; | |
| } | |
| #endif | |
| //the socket must be bound or connected | |
| if (!sock || SOCKET_INVALID(sock->socket)) | |
| return GF_BAD_PARAM; | |
| if (! (sock->flags & GF_SOCK_NON_BLOCKING)) { | |
| //check write | |
| GF_Err e = poll_select(sock, GF_SK_SELECT_WRITE, sock->usec_wait, GF_FALSE); | |
| if (e) return e; | |
| } | |
| //direct writing | |
| count = 0; | |
| while (count < length) { | |
| int sflags = 0; | |
| #ifdef MSG_NOSIGNAL | |
| sflags = MSG_NOSIGNAL; | |
| #endif | |
| if (sock->flags & GF_SOCK_HAS_PEER) { | |
| res = (s32) sendto(sock->socket, (char *) buffer+count, length - count, 0, (struct sockaddr *) &sock->dest_addr, sock->dest_addr_len); | |
| } else if (address && address_len) { | |
| res = (s32) sendto(sock->socket, (char *) buffer+count, length - count, 0, (struct sockaddr *) address, address_len); | |
| } else { | |
| res = (s32) send(sock->socket, (char *) buffer+count, length - count, sflags); | |
| } | |
| if (res == SOCKET_ERROR) { | |
| switch (res = LASTSOCKERROR) { | |
| case EAGAIN: | |
| case EINTR: | |
| return GF_IP_NETWORK_EMPTY; | |
| #ifndef __SYMBIAN32__ | |
| case ENOTCONN: | |
| case ECONNRESET: | |
| case EPIPE: | |
| GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[socket] send failure: %s\n", gf_errno_str(LASTSOCKERROR))); | |
| return GF_IP_CONNECTION_CLOSED; | |
| #endif | |
| #ifndef __DARWIN__ | |
| case EPROTOTYPE: | |
| return GF_IP_NETWORK_EMPTY; | |
| #endif | |
| case ENOBUFS: | |
| GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[socket] send failure: %s\n", gf_errno_str(LASTSOCKERROR))); | |
| return GF_BUFFER_TOO_SMALL; | |
| default: | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[socket] send failure: %s\n", gf_errno_str(LASTSOCKERROR))); | |
| return GF_IP_NETWORK_FAILURE; | |
| } | |
| } | |
| count += res; | |
| if (written) *written += res; | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[socket] sent %u bytes\n", count )); | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sk_send_ex(GF_Socket *sock, const u8 *buffer, u32 length, u32 *written) | |
| { | |
| return gf_sk_send_internal(sock, buffer, length, NULL, 0, written); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sk_send(GF_Socket *sock, const u8 *buffer, u32 length) | |
| { | |
| return gf_sk_send_internal(sock, buffer, length, NULL, 0, NULL); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sk_send_to(GF_Socket *sock, const u8 *buffer, u32 length, const u8 *addr, u32 addr_len, u32 *written) | |
| { | |
| return gf_sk_send_internal(sock, buffer, length, addr, addr_len, written); | |
| } | |
| GF_Err gf_sk_select(GF_Socket *sock, GF_SockSelectMode mode) | |
| { | |
| //the socket must be bound or connected | |
| if (!sock || SOCKET_INVALID(sock->socket)) | |
| return GF_BAD_PARAM; | |
| //check write and read | |
| return poll_select(sock, mode, sock->usec_wait, GF_FALSE); | |
| } | |
| GF_EXPORT | |
| u32 gf_sk_is_multicast_address(const char *multi_IPAdd) | |
| { | |
| if (!multi_IPAdd) return 0; | |
| #ifdef GPAC_HAS_IPV6 | |
| u32 val; | |
| char *sep; | |
| struct addrinfo *res; | |
| if (gf_net_is_ipv6(multi_IPAdd)) { | |
| /*IPV6 multicast address*/ | |
| sep = strchr(multi_IPAdd, ':'); | |
| if (sep) sep = strchr(multi_IPAdd, ':'); | |
| if (sep && !strnicmp(multi_IPAdd, "ff", 2)) return 1; | |
| //mapped address | |
| if (!strnicmp(multi_IPAdd, "::ffff:", 7)) multi_IPAdd+=7; | |
| /*ipv4 multicast address*/ | |
| res = gf_sk_get_ipv6_addr((char*)multi_IPAdd, 7000, AF_UNSPEC, AI_PASSIVE, SOCK_DGRAM); | |
| if (!res) return 0; | |
| val = 0; | |
| if (res->ai_addr->sa_family == AF_INET) { | |
| val = IN_MULTICAST(ntohl(((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr)); | |
| } else if (res->ai_addr->sa_family == AF_INET6) { | |
| val = IN6_IS_ADDR_MULTICAST(& ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr); | |
| } | |
| freeaddrinfo(res); | |
| return val; | |
| } | |
| #endif | |
| return ((htonl(inet_addr(multi_IPAdd)) >> 8) & 0x00f00000) == 0x00e00000; | |
| } | |
| static GF_Err sk_join_ipv4(GF_Socket *sock, struct ip_mreq *M_req, u32 TTL, const char **src_ip_inc, u32 nb_src_ip_inc, const char **src_ip_exc, u32 nb_src_ip_exc) | |
| { | |
| s32 ret; | |
| u32 flag; | |
| if (nb_src_ip_inc) { | |
| #ifdef IP_ADD_SOURCE_MEMBERSHIP | |
| u32 i; | |
| struct ip_mreq_source M_req_src; | |
| M_req_src.imr_multiaddr = M_req->imr_multiaddr; | |
| M_req_src.imr_interface = M_req->imr_interface; | |
| for (i=0; i<nb_src_ip_inc; i++) { | |
| if (gf_net_is_ipv6(src_ip_inc[i])) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[core] IPv6 SSM source on IPv4 Mcast address, ignoring\n", src_ip_inc[i])); | |
| continue; | |
| } | |
| M_req_src.imr_sourceaddr.s_addr = inet_addr(src_ip_inc[i]); | |
| ret = setsockopt(sock->socket, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *) &M_req_src, sizeof(M_req_src) ); | |
| if (ret == SOCKET_ERROR) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[core] cannot add source membership for %s: %s\n", src_ip_inc[i], gf_errno_str(LASTSOCKERROR))); | |
| return GF_IP_CONNECTION_FAILURE; | |
| } | |
| } | |
| #else | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[core] SSM IPv4 not supported\n")); | |
| return GF_NOT_SUPPORTED; | |
| #endif | |
| } else { | |
| ret = setsockopt(sock->socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) M_req, sizeof(*M_req)); | |
| if (ret == SOCKET_ERROR) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[core] cannot join multicast: %s\n", gf_errno_str(LASTSOCKERROR))); | |
| return GF_IP_CONNECTION_FAILURE; | |
| } | |
| } | |
| /*set the Time To Live*/ | |
| if (TTL) { | |
| ret = setsockopt(sock->socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&TTL, sizeof(TTL)); | |
| if (ret == SOCKET_ERROR) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[Socket] Cannot set TTL: %s\n", gf_errno_str(LASTSOCKERROR) )); | |
| return GF_IP_CONNECTION_FAILURE; | |
| } | |
| } | |
| /*enable loopback*/ | |
| flag = 1; | |
| ret = setsockopt(sock->socket, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &flag, sizeof(flag)); | |
| if (ret == SOCKET_ERROR) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[Socket] Cannot enable multicast loop: %s - ignoring\n", gf_errno_str(LASTSOCKERROR) )); | |
| } | |
| #ifdef IP_BLOCK_SOURCE | |
| if (nb_src_ip_exc) { | |
| u32 i, j; | |
| struct ip_mreq_source M_req_exc; | |
| M_req_exc.imr_multiaddr = M_req->imr_multiaddr; | |
| M_req_exc.imr_interface = M_req->imr_interface; | |
| for (i=0; i<nb_src_ip_exc; i++) { | |
| Bool match=GF_FALSE; | |
| if (gf_net_is_ipv6(src_ip_exc[i])) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[core] IPv6 SSM source on IPv4 Mcast address, ignoring\n", src_ip_exc[i])); | |
| continue; | |
| } | |
| for (j=0; j<nb_src_ip_inc; j++) { | |
| if (!strcmp(src_ip_inc[j], src_ip_exc[i])) match=GF_TRUE; | |
| } | |
| if (match) continue; | |
| M_req_exc.imr_sourceaddr.s_addr = inet_addr(src_ip_exc[i]); | |
| ret = setsockopt(sock->socket, IPPROTO_IP, IP_BLOCK_SOURCE, (char *) &M_req_exc, sizeof(M_req_exc) ); | |
| if (ret == SOCKET_ERROR) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[core] cannot block source membership for %s: %s\n", src_ip_exc[i], gf_errno_str(LASTSOCKERROR))); | |
| return GF_IP_CONNECTION_FAILURE; | |
| } | |
| } | |
| } | |
| #else | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[core] SSM IPv4 not supported\n")); | |
| #endif | |
| return GF_OK; | |
| } | |
| static u32 inet_addr_from_name(const char *local_interface) | |
| { | |
| if (!local_interface) return htonl(INADDR_ANY); | |
| if (strchr(local_interface, '.')) | |
| return inet_addr(local_interface); | |
| Bool name_match = GF_FALSE; | |
| u32 ret = 0; | |
| #if defined(GPAC_WIN_HAS_ADAPTER_INFO) | |
| IP_ADAPTER_ADDRESSES *AdapterInfo = malloc(sizeof(IP_ADAPTER_ADDRESSES)); | |
| IP_ADAPTER_ADDRESSES *ifa = NULL; | |
| ULONG ulOutBufLen = sizeof(AdapterInfo); | |
| if (AdapterInfo && (GetAdaptersAddresses(AF_INET, 0, NULL, AdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)) { | |
| free(AdapterInfo); | |
| AdapterInfo = malloc(sizeof(IP_ADAPTER_ADDRESSES)*ulOutBufLen); | |
| } | |
| if (AdapterInfo) | |
| GetAdaptersAddresses(AF_INET, 0, NULL, AdapterInfo, &ulOutBufLen); | |
| ifa = AdapterInfo; | |
| while (ifa) { | |
| Bool match = GF_FALSE; | |
| if (!strcmp(ifa->AdapterName, local_interface)) match = GF_FALSE; | |
| char *name = gf_wcs_to_utf8(ifa->FriendlyName); | |
| if (name) { | |
| if (!strcmp(name, local_interface)) match = GF_TRUE; | |
| gf_free(name); | |
| } | |
| if (match) name_match = GF_TRUE; | |
| if (!match || !ifa->FirstUnicastAddress || !ifa->FirstUnicastAddress->Address.lpSockaddr) { | |
| ifa = ifa->Next; | |
| continue; | |
| } | |
| ret = ((struct sockaddr_in*)ifa->FirstUnicastAddress->Address.lpSockaddr)->sin_addr.s_addr; | |
| break; | |
| } | |
| if (AdapterInfo) | |
| free(AdapterInfo); | |
| #endif | |
| #if defined(GPAC_HAS_IFADDRS) | |
| struct ifaddrs *ifap=NULL, *res; | |
| getifaddrs(&ifap); | |
| res = ifap; | |
| while (res) { | |
| if (res->ifa_name && !strcmp(res->ifa_name, local_interface)) { | |
| name_match = GF_TRUE; | |
| if (res->ifa_addr && (res->ifa_addr->sa_family==AF_INET)) { | |
| ret = ((struct sockaddr_in*)res->ifa_addr)->sin_addr.s_addr; | |
| break; | |
| } | |
| } | |
| res = res->ifa_next; | |
| } | |
| freeifaddrs(ifap); | |
| #endif | |
| if (!name_match) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[core] No such interface %s\n", local_interface)); | |
| return 0xFFFFFFFF; | |
| } | |
| if (!ret) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[core] Interface %s found but has no IPv4 addressing, using INADDR_ANY\n", local_interface)); | |
| return htonl(INADDR_ANY); | |
| } | |
| return ret; | |
| } | |
| GF_EXPORT | |
| Bool gf_net_enum_interfaces(gf_net_ifce_enum do_enum, void *enum_cbk) | |
| { | |
| #if defined(GPAC_WIN_HAS_ADAPTER_INFO) | |
| IP_ADAPTER_ADDRESSES *AdapterInfo = malloc(sizeof(IP_ADAPTER_ADDRESSES)); | |
| IP_ADAPTER_ADDRESSES *ifa = NULL; | |
| ULONG ulOutBufLen = sizeof(AdapterInfo); | |
| if (AdapterInfo && (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, AdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)) { | |
| free(AdapterInfo); | |
| AdapterInfo = malloc(sizeof(IP_ADAPTER_ADDRESSES)*ulOutBufLen); | |
| } | |
| if (AdapterInfo) | |
| GetAdaptersAddresses(AF_UNSPEC, 0, NULL, AdapterInfo, &ulOutBufLen); | |
| ifa = AdapterInfo; | |
| while (ifa) { | |
| if (ifa->FirstUnicastAddress) { | |
| char szIP[100]; | |
| szIP[0] = 0; | |
| u32 flags = 0; | |
| if (ifa->OperStatus == 1) flags |= GF_NETIF_ACTIVE; | |
| if (ifa->Flags & IP_ADAPTER_NO_MULTICAST) flags |= GF_NETIF_NO_MCAST; | |
| if (ifa->Flags & IP_ADAPTER_RECEIVE_ONLY) flags |= GF_NETIF_RECV_ONLY; | |
| if (ifa->IfType == IF_TYPE_SOFTWARE_LOOPBACK) flags |= GF_NETIF_LOOPBACK; | |
| Bool abort=GF_FALSE; | |
| IP_ADAPTER_UNICAST_ADDRESS_LH *ipadd = ifa->FirstUnicastAddress; | |
| while (ipadd) { | |
| u32 sa_fam = ipadd->Address.lpSockaddr ? ipadd->Address.lpSockaddr->sa_family : AF_UNSPEC; | |
| if (ipadd->Flags & IP_ADAPTER_ADDRESS_TRANSIENT) sa_fam = AF_UNSPEC; | |
| if ((sa_fam == AF_INET) || (sa_fam == AF_INET6)) { | |
| my_inet_ntop(sa_fam, ipadd->Address.lpSockaddr, szIP, 100); | |
| char *name = gf_wcs_to_utf8(ifa->FriendlyName); | |
| Bool res = do_enum(enum_cbk, name ? name : ifa->AdapterName, szIP[0] ? szIP : NULL, flags | ((sa_fam==AF_INET6) ? GF_NETIF_IPV6 : 0)); | |
| if (name) gf_free(name); | |
| if (res) break; | |
| } | |
| ipadd = ipadd->Next; | |
| } | |
| if (abort) break; | |
| } | |
| ifa = ifa->Next; | |
| } | |
| if (AdapterInfo) | |
| free(AdapterInfo); | |
| return GF_TRUE; | |
| #endif | |
| #if defined(GPAC_HAS_IFADDRS) | |
| struct ifaddrs *ifap=NULL, *res; | |
| getifaddrs(&ifap); | |
| res = ifap; | |
| while (res) { | |
| if (res->ifa_addr) { | |
| char szIP[100]; | |
| szIP[0] = 0; | |
| u32 flags=0; | |
| if (res->ifa_flags & IFF_UP) flags |= GF_NETIF_ACTIVE; | |
| if (res->ifa_flags & IFF_LOOPBACK) flags |= GF_NETIF_LOOPBACK; | |
| if (!(res->ifa_flags & IFF_MULTICAST)) flags |= GF_NETIF_NO_MCAST; | |
| if (res->ifa_addr->sa_family==AF_INET) { | |
| inet_ntop(AF_INET, &( ((struct sockaddr_in *)res->ifa_addr)->sin_addr), szIP, 100); | |
| if (do_enum(enum_cbk, res->ifa_name, szIP[0] ? szIP : NULL, flags)) | |
| break; | |
| } | |
| if (res->ifa_addr->sa_family==AF_INET6) { | |
| inet_ntop(AF_INET6, &( ((struct sockaddr_in6 *)res->ifa_addr)->sin6_addr), szIP, 100); | |
| if (do_enum(enum_cbk, res->ifa_name, szIP[0] ? szIP : NULL, flags|GF_NETIF_IPV6)) | |
| break; | |
| } | |
| } | |
| res = res->ifa_next; | |
| } | |
| freeifaddrs(ifap); | |
| return GF_TRUE; | |
| #endif | |
| return GF_FALSE; | |
| } | |
| typedef struct | |
| { | |
| const char *ip_or_name; | |
| char *res_v6, *res_v4; | |
| } GetIPInfo; | |
| static Bool get_ifce_enum(void *cbk, const char *name, const char *IP, u32 flags) | |
| { | |
| GetIPInfo *info = (GetIPInfo*)cbk; | |
| if (strcmp(info->ip_or_name, name) && strcmp(info->ip_or_name, IP)) return GF_FALSE; | |
| if (flags & GF_NETIF_IPV6) { | |
| if (!info->res_v6) info->res_v6 = gf_strdup(IP); | |
| } else { | |
| if (!info->res_v4) info->res_v4 = gf_strdup(IP); | |
| } | |
| return GF_FALSE; | |
| } | |
| Bool gf_net_get_adapter_ip(const char *ip_or_name, char **ipv4, char **ipv6) | |
| { | |
| GetIPInfo info; | |
| info.ip_or_name = ip_or_name; | |
| info.res_v4 = info.res_v6 = NULL; | |
| if (ipv4) *ipv4 = NULL; | |
| if (ipv6) *ipv6 = NULL; | |
| if (!ip_or_name) return GF_FALSE; | |
| Bool res = gf_net_enum_interfaces(get_ifce_enum, &info); | |
| if (!res) return GF_FALSE; | |
| if (ipv4) *ipv4 = info.res_v4; | |
| else if (info.res_v4) gf_free(info.res_v4); | |
| if (ipv6) *ipv6 = info.res_v6; | |
| else if (info.res_v6) gf_free(info.res_v6); | |
| return GF_TRUE; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sk_setup_multicast_ex(GF_Socket *sock, const char *multi_IPAdd, u16 MultiPortNumber, u32 TTL, Bool NoBind, const char *ifce_ip_or_name, | |
| const char **src_ip_inc, u32 nb_src_ip_inc, const char **src_ip_exc, u32 nb_src_ip_exc) | |
| { | |
| s32 ret; | |
| struct ip_mreq M_req; | |
| u32 optval; | |
| #ifdef GPAC_HAS_IPV6 | |
| struct sockaddr *addr; | |
| Bool mcast_ipv6 = GF_FALSE; | |
| Bool ifce_ipv6 = GF_FALSE; | |
| u32 type; | |
| #endif | |
| GF_Err e; | |
| u32 local_add_id; | |
| //socket must not be created | |
| if (!sock || !SOCKET_INVALID(sock->socket)) return GF_BAD_PARAM; | |
| if (TTL > 255) TTL = 255; | |
| /*check the address*/ | |
| if (!gf_sk_is_multicast_address(multi_IPAdd)) return GF_BAD_PARAM; | |
| #ifdef GPAC_HAS_IPV6 | |
| mcast_ipv6 = gf_net_is_ipv6(multi_IPAdd); | |
| ifce_ipv6 = gf_net_is_ipv6(ifce_ip_or_name); | |
| if (ifce_ip_or_name && (ifce_ip_or_name[0]=='+')) { | |
| ifce_ip_or_name += 1; | |
| ifce_ipv6 = GF_TRUE; | |
| } | |
| if (ifce_ip_or_name && !ifce_ip_or_name[0]) | |
| ifce_ip_or_name=NULL; | |
| type = (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM; | |
| if (mcast_ipv6 || ifce_ipv6) { | |
| Bool is_mapped_v4=GF_FALSE; | |
| struct addrinfo *res, *aip; | |
| struct ipv6_mreq M_reqV6; | |
| memset(&M_reqV6, 0, sizeof(struct ipv6_mreq)); | |
| //get interface index - force IPV6 when getting address and store result | |
| if (ifce_ip_or_name) { | |
| #if defined(GPAC_WIN_HAS_ADAPTER_INFO) | |
| IP_ADAPTER_ADDRESSES *AdapterInfo = malloc(sizeof(IP_ADAPTER_ADDRESSES)); | |
| ULONG ulOutBufLen = sizeof(AdapterInfo); | |
| if (AdapterInfo && (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, AdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)) { | |
| free(AdapterInfo); | |
| AdapterInfo = malloc(sizeof(IP_ADAPTER_ADDRESSES)*ulOutBufLen); | |
| } | |
| if (AdapterInfo) | |
| GetAdaptersAddresses(AF_UNSPEC, 0, NULL, AdapterInfo, &ulOutBufLen); | |
| IP_ADAPTER_ADDRESSES *ifa = AdapterInfo; | |
| while (ifa) { | |
| //match by adapter name | |
| if (!strcmp(ifa->AdapterName, ifce_ip_or_name)) { | |
| M_reqV6.ipv6mr_interface = ifa->Ipv6IfIndex; | |
| break; | |
| } | |
| char *name = gf_wcs_to_utf8(ifa->FriendlyName); | |
| if (name && !strcmp(name, ifce_ip_or_name)) { | |
| M_reqV6.ipv6mr_interface = ifa->Ipv6IfIndex; | |
| gf_free(name); | |
| break; | |
| } | |
| if (name) gf_free(name); | |
| name = gf_wcs_to_utf8(ifa->Description); | |
| if (!strcmp(name, ifce_ip_or_name)) { | |
| M_reqV6.ipv6mr_interface = ifa->Ipv6IfIndex; | |
| gf_free(name); | |
| break; | |
| } | |
| if (name) gf_free(name); | |
| //match by IP | |
| IP_ADAPTER_UNICAST_ADDRESS_LH *ipadd = ifa->FirstUnicastAddress; | |
| while (ipadd) { | |
| u32 sa_fam = ipadd->Address.lpSockaddr ? ipadd->Address.lpSockaddr->sa_family : AF_UNSPEC; | |
| if ((sa_fam == AF_INET) || (sa_fam == AF_INET6)) { | |
| char szIP[100]; | |
| my_inet_ntop(sa_fam, ipadd->Address.lpSockaddr, szIP, 100); | |
| if (!strcmp(szIP, ifce_ip_or_name)) { | |
| M_reqV6.ipv6mr_interface = ifa->Ipv6IfIndex; | |
| break; | |
| } | |
| } | |
| ipadd = ipadd->Next; | |
| } | |
| ifa = ifa->Next; | |
| } | |
| if (AdapterInfo) free(AdapterInfo); | |
| #endif | |
| #if defined(GPAC_HAS_IFADDRS) | |
| //get sockaddr if this is an IP address and compare with enumerated interface | |
| aip = NULL; | |
| if (ifce_ip_or_name && strpbrk(ifce_ip_or_name, ".:")) { | |
| aip = gf_sk_get_ipv6_addr(ifce_ip_or_name, MultiPortNumber, ifce_ipv6 ? AF_INET6 : AF_UNSPEC, AI_PASSIVE, type); | |
| } | |
| struct ifaddrs *ifap=NULL, *ifa; | |
| getifaddrs(&ifap); | |
| ifa = ifap; | |
| while (ifa) { | |
| //match by ifce name | |
| if (ifa->ifa_addr && !strcmp(ifa->ifa_name, ifce_ip_or_name) | |
| && ((ifa->ifa_addr->sa_family == AF_INET) || (ifa->ifa_addr->sa_family == AF_INET6)) | |
| ) { | |
| M_reqV6.ipv6mr_interface = if_nametoindex(ifa->ifa_name); | |
| break; | |
| } | |
| //match by ifce IP | |
| if (aip && ifa->ifa_addr) { | |
| struct sockaddr_in6 *in6_a = (struct sockaddr_in6 *)aip->ai_addr; | |
| struct sockaddr_in6 *in6_b = (struct sockaddr_in6 *)ifa->ifa_addr; | |
| if ((in6_a->sin6_family == in6_b->sin6_family) | |
| && !memcmp(&in6_a->sin6_addr, &in6_b->sin6_addr, sizeof(struct in6_addr)) | |
| ) { | |
| M_reqV6.ipv6mr_interface = if_nametoindex(ifa->ifa_name); | |
| break; | |
| } | |
| } | |
| ifa = ifa->ifa_next; | |
| } | |
| if (ifap) | |
| freeifaddrs(ifap); | |
| if (aip) | |
| freeaddrinfo(aip); | |
| #endif | |
| if (!M_reqV6.ipv6mr_interface) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[core] Failed to query interface index for %s\n", ifce_ip_or_name )); | |
| return GF_IP_CONNECTION_FAILURE; | |
| } | |
| } | |
| //do not specify local_interface_ip address | |
| res = gf_sk_get_ipv6_addr(NULL, MultiPortNumber, ifce_ipv6 ? AF_INET6 : AF_UNSPEC, AI_PASSIVE, type); | |
| if (!res) return GF_IP_CONNECTION_FAILURE; | |
| /*for all interfaces*/ | |
| for (aip=res; aip!=NULL; aip=aip->ai_next) { | |
| if (type != (u32) aip->ai_socktype) continue; | |
| //we have a v4 multicast address | |
| if (!gf_net_is_ipv6(multi_IPAdd)) { | |
| //if not v4 and next is v4, use next | |
| if ((aip->ai_family!=PF_INET) && aip->ai_next && (aip->ai_next->ai_family==PF_INET)) continue; | |
| } else { | |
| //we want v6, if v4 and next is v6, use next | |
| if ((aip->ai_family==PF_INET) && aip->ai_next && (aip->ai_next->ai_family==PF_INET6)) continue; | |
| } | |
| sock->socket = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); | |
| if (SOCKET_INVALID(sock->socket)) { | |
| continue; | |
| } | |
| /*enable address reuse*/ | |
| optval = 1; | |
| setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, (const char *) &optval, sizeof(optval)); | |
| #ifdef SO_REUSEPORT | |
| optval = 1; | |
| setsockopt(sock->socket, SOL_SOCKET, SO_REUSEPORT, SSO_CAST &optval, sizeof(optval)); | |
| #endif | |
| if (!mcast_ipv6 && (aip->ai_family == PF_INET6)) { | |
| optval = 0; | |
| ret = setsockopt(sock->socket, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&optval, sizeof(optval)); | |
| if (ret == SOCKET_ERROR) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[Socket] Cannot disable ipv6only: %s\n", gf_errno_str(LASTSOCKERROR))); | |
| } | |
| } | |
| if (!NoBind) { | |
| ret = bind(sock->socket, (struct sockaddr *) aip->ai_addr, (int) aip->ai_addrlen); | |
| if (ret == SOCKET_ERROR) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[core] cannot bind socket: %s\n", gf_errno_str(LASTSOCKERROR))); | |
| return GF_IP_CONNECTION_FAILURE; | |
| } | |
| } | |
| if (aip->ai_family==PF_INET6) sock->flags |= GF_SOCK_IS_IPV6; | |
| else sock->flags &= ~GF_SOCK_IS_IPV6; | |
| break; | |
| } | |
| freeaddrinfo(res); | |
| if (SOCKET_INVALID(sock->socket) ) return GF_IP_CONNECTION_FAILURE; | |
| /*TODO: copy over other properties (recption buffer size & co)*/ | |
| if (sock->flags & GF_SOCK_NON_BLOCKING) | |
| gf_sk_set_block_mode(sock, GF_TRUE); | |
| res = gf_sk_get_ipv6_addr(multi_IPAdd, MultiPortNumber, (sock->flags & GF_SOCK_IS_IPV6) ? AF_INET6 : AF_UNSPEC, 0, SOCK_DGRAM); | |
| if (!res) { | |
| if (sock->flags & GF_SOCK_IS_IPV6) { | |
| char szIP[100]; | |
| gf_strcpy(szIP, "::ffff:"); | |
| gf_strcat(szIP, multi_IPAdd); | |
| is_mapped_v4 = GF_TRUE; | |
| res = gf_sk_get_ipv6_addr(szIP, MultiPortNumber, AF_INET6, 0, SOCK_DGRAM); | |
| } | |
| if (!res) | |
| return GF_IP_CONNECTION_FAILURE; | |
| } | |
| //copy for sendto | |
| memcpy(&sock->dest_addr, res->ai_addr, res->ai_addrlen); | |
| sock->dest_addr_len = (u32) res->ai_addrlen; | |
| freeaddrinfo(res); | |
| addr = (struct sockaddr *)&sock->dest_addr; | |
| //ipv4 | |
| if (is_mapped_v4) { | |
| local_add_id = inet_addr_from_name(ifce_ip_or_name); | |
| if (local_add_id==0xFFFFFFFF) return GF_IP_CONNECTION_FAILURE; | |
| M_req.imr_multiaddr.s_addr = inet_addr(multi_IPAdd); | |
| M_req.imr_interface.s_addr = local_add_id; | |
| e = sk_join_ipv4(sock, &M_req, TTL, src_ip_inc, nb_src_ip_inc, src_ip_exc, nb_src_ip_exc); | |
| if (e) return e; | |
| //keep sock->dest_addr untouched so that we still have a ipv6 sockaddr | |
| sock->flags |= GF_SOCK_IS_MULTICAST | GF_SOCK_HAS_PEER; | |
| return GF_OK; | |
| } | |
| else if (addr->sa_family != AF_INET6) { | |
| return GF_IP_CONNECTION_FAILURE; | |
| } | |
| memcpy(&M_reqV6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr)); | |
| /*set send IF index*/ | |
| optval = M_reqV6.ipv6mr_interface; | |
| ret = setsockopt(sock->socket, IPPROTO_IPV6, IPV6_MULTICAST_IF, (const char *)&optval, sizeof(optval)); | |
| if (ret == SOCKET_ERROR) { | |
| } | |
| if (nb_src_ip_inc) { | |
| #ifdef MCAST_JOIN_SOURCE_GROUP | |
| u32 i; | |
| struct group_source_req M_reqV6_src; | |
| memset(&M_reqV6_src, 0, sizeof(struct group_source_req)); | |
| M_reqV6_src.gsr_interface = 0; | |
| M_reqV6_src.gsr_group = sock->dest_addr; | |
| for (i=0; i<nb_src_ip_inc; i++) { | |
| res = gf_sk_get_ipv6_addr(src_ip_inc[i], MultiPortNumber, AF_INET6, 0, SOCK_DGRAM); | |
| if (!res) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[core] Failed to get address info for SSM source %s, ignoring\n", src_ip_inc[i])); | |
| continue; | |
| } | |
| memcpy(&M_reqV6_src.gsr_source, res->ai_addr, res->ai_addrlen); | |
| freeaddrinfo(res); | |
| ret = setsockopt(sock->socket, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP, (char *) &M_reqV6_src, sizeof(M_reqV6_src) ); | |
| if (ret == SOCKET_ERROR) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[core] cannot join source group %s: %s\n", src_ip_inc[i], gf_errno_str(LASTSOCKERROR))); | |
| return GF_IP_CONNECTION_FAILURE; | |
| } | |
| } | |
| #else | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[core] SSM IPv6 not supported\n")); | |
| return GF_NOT_SUPPORTED; | |
| #endif | |
| } else { | |
| ret = setsockopt(sock->socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *) &M_reqV6, sizeof(M_reqV6)); | |
| if (ret == SOCKET_ERROR) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[core] Failed to join multicast: %s\n", gf_errno_str(LASTSOCKERROR) )); | |
| return GF_IP_CONNECTION_FAILURE; | |
| } | |
| } | |
| /*set TTL*/ | |
| if (TTL != 0) { | |
| ret = setsockopt(sock->socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &TTL, sizeof(TTL)); | |
| if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; | |
| } | |
| /*enable loopback*/ | |
| optval = 1; | |
| ret = setsockopt(sock->socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &optval, sizeof(optval)); | |
| if (ret == SOCKET_ERROR) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[Socket] Cannot enable multicast loop: %s\n", gf_errno_str(LASTSOCKERROR) )); | |
| } | |
| if (nb_src_ip_exc) { | |
| #ifdef MCAST_BLOCK_SOURCE | |
| u32 i, j; | |
| struct group_source_req M_reqV6_src; | |
| M_reqV6_src.gsr_interface = 0; | |
| M_reqV6_src.gsr_group = sock->dest_addr; | |
| for (i=0; i<nb_src_ip_exc; i++) { | |
| Bool match=GF_FALSE; | |
| for (j=0; j<nb_src_ip_inc; j++) { | |
| if (!strcmp(src_ip_inc[j], src_ip_exc[i])) match=GF_TRUE; | |
| } | |
| if (match) continue; | |
| res = gf_sk_get_ipv6_addr(src_ip_exc[i], MultiPortNumber, AF_INET6, 0, SOCK_DGRAM); | |
| if (!res) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[core] Failed to get address info for SSM excluded source %s, ignoring\n", src_ip_exc[i])); | |
| continue; | |
| } | |
| memcpy(&M_reqV6_src.gsr_source, res->ai_addr, res->ai_addrlen); | |
| freeaddrinfo(res); | |
| ret = setsockopt(sock->socket, IPPROTO_IPV6, MCAST_BLOCK_SOURCE, (char *) &M_reqV6_src, sizeof(M_reqV6_src) ); | |
| if (ret == SOCKET_ERROR) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[core] cannot block source membership for %s: %s\n", src_ip_exc[i], gf_errno_str(LASTSOCKERROR))); | |
| return GF_IP_CONNECTION_FAILURE; | |
| } | |
| } | |
| #else | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[core] SSM IPv6 not supported\n")); | |
| #endif | |
| } | |
| sock->flags |= GF_SOCK_IS_MULTICAST | GF_SOCK_HAS_PEER; | |
| return GF_OK; | |
| } | |
| #endif | |
| #ifndef GPAC_DISABLE_NETCAP | |
| if (sock->cap_info && sock->cap_info->nf->cap_mode) { | |
| //in GPC mode, store in host port/addr | |
| if (sock->cap_info->nf->cap_mode==NETCAP_GPAC) { | |
| sock->cap_info->host_port = MultiPortNumber; | |
| sock->cap_info->host_addr_v4 = inet_addr(multi_IPAdd); | |
| } else { | |
| sock->cap_info->host_port = MultiPortNumber; | |
| sock->cap_info->host_addr_v4 = inet_addr_from_name(ifce_ip_or_name ? ifce_ip_or_name : "127.0.0.1"); | |
| sock->cap_info->peer_port = MultiPortNumber; | |
| sock->cap_info->peer_addr_v4 = inet_addr(multi_IPAdd); | |
| } | |
| sock->flags &= ~GF_SOCK_IS_IPV6; | |
| sock->flags |= GF_SOCK_IS_MULTICAST; | |
| return GF_OK; | |
| } | |
| #endif | |
| //IPv4 setup | |
| sock->socket = socket(AF_INET, (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM, 0); | |
| if (SOCKET_INVALID(sock->socket)) return GF_IP_NETWORK_FAILURE; | |
| if (sock->flags & GF_SOCK_NON_BLOCKING) | |
| gf_sk_set_block_mode(sock, GF_TRUE); | |
| sock->flags &= ~GF_SOCK_IS_IPV6; | |
| /*enable address reuse*/ | |
| optval = 1; | |
| ret = setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, SSO_CAST &optval, sizeof(optval)); | |
| if (ret == SOCKET_ERROR) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[core] Failed to set SO_REUSEADDR: %s\n", gf_errno_str(LASTSOCKERROR) )); | |
| } | |
| #ifdef SO_REUSEPORT | |
| optval = 1; | |
| ret = setsockopt(sock->socket, SOL_SOCKET, SO_REUSEPORT, SSO_CAST &optval, sizeof(optval)); | |
| if (ret == SOCKET_ERROR) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[core] Failed to set SO_REUSEPORT: %s\n", gf_errno_str(LASTSOCKERROR) )); | |
| } | |
| #endif | |
| local_add_id = inet_addr_from_name(ifce_ip_or_name); | |
| if (local_add_id==0xFFFFFFFF) return GF_IP_CONNECTION_FAILURE; | |
| if (!NoBind) { | |
| struct sockaddr_in local_address; | |
| memset(&local_address, 0, sizeof(struct sockaddr_in )); | |
| local_address.sin_family = AF_INET; | |
| local_address.sin_addr.s_addr = htonl(INADDR_ANY); | |
| local_address.sin_port = htons( MultiPortNumber); | |
| ret = bind(sock->socket, (struct sockaddr *) &local_address, sizeof(local_address)); | |
| if (ret == SOCKET_ERROR) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[core] Failed to bind socket: %s\n", gf_errno_str(LASTSOCKERROR) )); | |
| return GF_IP_CONNECTION_FAILURE; | |
| } | |
| /*setup local interface*/ | |
| if (ifce_ip_or_name) { | |
| ret = setsockopt(sock->socket, IPPROTO_IP, IP_MULTICAST_IF, (void *) &local_add_id, sizeof(local_add_id)); | |
| if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; | |
| } | |
| } | |
| /*now join the multicast*/ | |
| M_req.imr_multiaddr.s_addr = inet_addr(multi_IPAdd); | |
| M_req.imr_interface.s_addr = local_add_id; | |
| e = sk_join_ipv4(sock, &M_req, TTL, src_ip_inc, nb_src_ip_inc, src_ip_exc, nb_src_ip_exc); | |
| if (e) return e; | |
| #ifdef GPAC_HAS_IPV6 | |
| ((struct sockaddr_in *) &sock->dest_addr)->sin_family = AF_INET; | |
| ((struct sockaddr_in *) &sock->dest_addr)->sin_addr.s_addr = M_req.imr_multiaddr.s_addr; | |
| ((struct sockaddr_in *) &sock->dest_addr)->sin_port = htons( MultiPortNumber); | |
| sock->dest_addr_len = sizeof(struct sockaddr); | |
| #else | |
| sock->dest_addr.sin_family = AF_INET; | |
| sock->dest_addr.sin_addr.s_addr = M_req.imr_multiaddr.s_addr; | |
| sock->dest_addr.sin_port = htons( MultiPortNumber); | |
| #endif | |
| sock->flags |= GF_SOCK_IS_MULTICAST | GF_SOCK_HAS_PEER; | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sk_setup_multicast(GF_Socket *sock, const char *multi_IPAdd, u16 MultiPortNumber, u32 TTL, Bool NoBind, const char *ifce_ip_or_name) | |
| { | |
| return gf_sk_setup_multicast_ex(sock, multi_IPAdd, MultiPortNumber, TTL, NoBind, ifce_ip_or_name, NULL, 0, NULL, 0); | |
| } | |
| #include <gpac/list.h> | |
| struct __tag_sock_group | |
| { | |
| GF_List *sockets; | |
| fd_set rgroup, wgroup; | |
| #ifdef GPAC_HAS_POLL | |
| u32 last_mask; | |
| u32 nb_fds, alloc_fds; | |
| GF_POLLFD *fds; | |
| #endif | |
| #ifndef GPAC_DISABLE_NETCAP | |
| u32 nb_nfs; | |
| u32 nb_socks; | |
| #endif | |
| }; | |
| GF_SockGroup *gf_sk_group_new() | |
| { | |
| GF_SockGroup *tmp; | |
| GF_SAFEALLOC(tmp, GF_SockGroup); | |
| if (!tmp) return NULL; | |
| FD_ZERO(&tmp->rgroup); | |
| FD_ZERO(&tmp->wgroup); | |
| #ifdef GPAC_HAS_POLL | |
| tmp->last_mask = POLLIN; | |
| #endif | |
| return tmp; | |
| } | |
| void gf_sk_group_del(GF_SockGroup *sg) | |
| { | |
| gf_list_del(sg->sockets); | |
| #ifdef GPAC_HAS_POLL | |
| if (sg->fds) gf_free(sg->fds); | |
| #endif | |
| gf_free(sg); | |
| } | |
| void gf_sk_group_register(GF_SockGroup *sg, GF_Socket *sk) | |
| { | |
| if (!sg || !sk) return; | |
| if (!sg->sockets) sg->sockets = gf_list_new(); | |
| if (gf_list_find(sg->sockets, sk)>=0) return; | |
| gf_list_add(sg->sockets, sk); | |
| #ifndef GPAC_DISABLE_NETCAP | |
| if (sk->cap_info) { | |
| sg->nb_nfs++; | |
| sg->nb_socks = gf_list_count(sg->sockets); | |
| if (sk->cap_info->nf && (gf_list_find(sk->cap_info->nf->read_socks, sk)<0)) | |
| gf_list_add(sk->cap_info->nf->read_socks, sk); | |
| return; | |
| } | |
| #endif | |
| #ifdef GPAC_HAS_POLL | |
| if (!sg->fds && !gpac_use_poll) | |
| return; | |
| if (sg->nb_fds + 1 > sg->alloc_fds) { | |
| sg->fds = gf_realloc(sg->fds, (sg->nb_fds+1) * sizeof(GF_POLLFD)); | |
| sg->alloc_fds = sg->nb_fds+1; | |
| } | |
| sg->fds[sg->nb_fds].fd = sk->socket; | |
| sg->fds[sg->nb_fds].events = sg->last_mask; | |
| sg->fds[sg->nb_fds].revents = 0; | |
| sk->poll_idx = sg->nb_fds+1; | |
| gf_assert(! SOCKET_INVALID(sg->fds[sg->nb_fds].fd) ); | |
| sg->nb_fds++; | |
| #endif | |
| } | |
| void gf_sk_group_unregister(GF_SockGroup *sg, GF_Socket *sk) | |
| { | |
| if (!sg || !sk) return; | |
| s32 pidx = gf_list_del_item(sg->sockets, sk); | |
| #ifndef GPAC_DISABLE_NETCAP | |
| if (sk->cap_info && sk->cap_info->nf->read_socks) { | |
| if (sg->nb_nfs && (pidx>=0)) | |
| sg->nb_nfs--; | |
| sg->nb_socks = gf_list_count(sg->sockets); | |
| if (sk == sk->cap_info->nf->read_sock_selected) { | |
| gf_bs_skip_bytes(sk->cap_info->nf->cap_bs, sk->cap_info->nf->pck_len); | |
| sk->cap_info->nf->dst_port = 0; | |
| sk->cap_info->nf->read_sock_selected=NULL; | |
| //do not load a new packet here as this could go into infinite loop when netcap-loop=-1 ... | |
| } | |
| gf_list_del_item(sk->cap_info->nf->read_socks, sk); | |
| return; | |
| } | |
| #endif | |
| if (!gf_list_count(sg->sockets)) { | |
| gf_list_del(sg->sockets); | |
| sg->sockets = NULL; | |
| #ifdef GPAC_HAS_POLL | |
| if (sg->fds) { | |
| gf_free(sg->fds); | |
| sg->fds = NULL; | |
| sg->nb_fds = sg->alloc_fds = 0; | |
| } | |
| #endif | |
| return; | |
| } | |
| if (pidx<0) return; | |
| #ifdef GPAC_HAS_POLL | |
| sk->poll_idx=0; | |
| if (!sg->fds) return; | |
| memmove(&sg->fds[pidx], &sg->fds[pidx+1], sizeof(GF_POLLFD) * (sg->nb_fds-pidx-1)); | |
| //reassign poll file descriptors and poll index | |
| sg->nb_fds--; | |
| while (1) { | |
| GF_Socket *asock = gf_list_get(sg->sockets, pidx); | |
| if (!asock) break; | |
| sg->fds[pidx].fd = asock->socket; | |
| asock->poll_idx = pidx+1; | |
| pidx++; | |
| } | |
| #endif | |
| } | |
| GF_Err gf_sk_group_select(GF_SockGroup *sg, u32 usec_wait, GF_SockSelectMode mode) | |
| { | |
| s32 ready; | |
| u32 i; | |
| struct timeval timeout; | |
| GF_Socket *sock; | |
| if (!sg->sockets) | |
| return GF_IP_NETWORK_EMPTY; | |
| #ifndef GPAC_DISABLE_NETCAP | |
| if (sg->nb_nfs) { | |
| u32 nb_closed=0; | |
| u32 nb_write=0; | |
| u32 nb_nofs=0; | |
| u32 nb_tcp=0; | |
| u64 now = gf_sys_clock_high_res(); | |
| for (i=0; i<sg->nb_socks; i++) { | |
| GF_Socket *s = gf_list_get(sg->sockets, i); | |
| if (!s->cap_info) { | |
| nb_nofs++; | |
| continue; | |
| } | |
| if (!s->cap_info->nf->read_socks) { | |
| nb_write++; | |
| continue; | |
| } | |
| gf_netcap_load_pck(s->cap_info->nf, now); | |
| if (!s->cap_info->nf->read_sock_selected) { | |
| if (s->cap_info->nf->is_eos) { | |
| if (s->flags & GF_SOCK_IS_TCP) nb_tcp++; | |
| nb_closed++; | |
| } | |
| continue; | |
| } | |
| if (s->cap_info->nf->rt && (s->cap_info->nf->pck_time > now)) | |
| continue; // GF_IP_NETWORK_EMPTY; | |
| if (s == s->cap_info->nf->read_sock_selected) | |
| return GF_OK; | |
| } | |
| if (nb_closed==sg->nb_socks) return nb_tcp ? GF_IP_CONNECTION_CLOSED : GF_EOS; | |
| if (nb_write) return GF_OK; | |
| if (!nb_nofs) return GF_IP_NETWORK_EMPTY; | |
| //fallthrough | |
| } | |
| #endif | |
| #ifdef GPAC_HAS_POLL | |
| if (sg->fds) { | |
| u32 mask = 0; | |
| if (mode == GF_SK_SELECT_BOTH) mask = POLLIN | POLLOUT; | |
| else if (mode == GF_SK_SELECT_READ) mask = POLLIN; | |
| else mask = POLLOUT; | |
| if (sg->last_mask != mask) { | |
| sg->last_mask = mask; | |
| for (i=0; i<sg->nb_fds; i++) { | |
| sg->fds[i].events = mask; | |
| gf_assert(! SOCKET_INVALID(sg->fds[i].fd) ); | |
| } | |
| } | |
| int res = poll(sg->fds, sg->nb_fds, usec_wait/1000); | |
| if (res<0) { | |
| switch (LASTSOCKERROR) { | |
| case EAGAIN: | |
| case EINTR: | |
| return GF_IP_NETWORK_EMPTY; | |
| default: | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[socket] cannot poll: %s\n", gf_errno_str(LASTSOCKERROR) )); | |
| return GF_IP_NETWORK_FAILURE; | |
| } | |
| } | |
| if (!res) | |
| return GF_IP_NETWORK_EMPTY; | |
| return GF_OK; | |
| } | |
| #endif | |
| i=0; | |
| u32 max_fd=0; | |
| fd_set *rgroup=NULL, *wgroup=NULL; | |
| FD_ZERO(&sg->rgroup); | |
| FD_ZERO(&sg->wgroup); | |
| switch (mode) { | |
| case GF_SK_SELECT_BOTH: | |
| rgroup = &sg->rgroup; | |
| wgroup = &sg->wgroup; | |
| break; | |
| case GF_SK_SELECT_READ: | |
| rgroup = &sg->rgroup; | |
| break; | |
| case GF_SK_SELECT_WRITE: | |
| wgroup = &sg->wgroup; | |
| break; | |
| } | |
| while ((sock = gf_list_enum(sg->sockets, &i))) { | |
| if (rgroup) | |
| FD_SET(sock->socket, rgroup); | |
| if (wgroup) | |
| FD_SET(sock->socket, wgroup); | |
| if (max_fd < (u32) sock->socket) max_fd = (u32) sock->socket; | |
| } | |
| timeout.tv_sec = 0; | |
| timeout.tv_usec = usec_wait; | |
| ready = select((int) max_fd+1, rgroup, wgroup, NULL, &timeout); | |
| if (ready == SOCKET_ERROR) { | |
| switch (LASTSOCKERROR) { | |
| case EBADF: | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[socket] cannot select, BAD descriptor\n")); | |
| return GF_IP_CONNECTION_CLOSED; | |
| case EAGAIN: | |
| case EINTR: | |
| return GF_IP_NETWORK_EMPTY; | |
| default: | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[socket] cannot select: %s\n", gf_errno_str(LASTSOCKERROR) )); | |
| return GF_IP_NETWORK_FAILURE; | |
| } | |
| } | |
| if (!ready) { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[socket] nothing to be read - ready %d\n", ready)); | |
| return GF_IP_NETWORK_EMPTY; | |
| } | |
| return GF_OK; | |
| } | |
| Bool gf_sk_group_sock_is_set(GF_SockGroup *sg, GF_Socket *sk, GF_SockSelectMode mode) | |
| { | |
| if (!sg || !sk) return GF_FALSE; | |
| #ifndef GPAC_DISABLE_NETCAP | |
| if (sk->cap_info) { | |
| //write, always true | |
| if (!sk->cap_info->nf->read_socks) return GF_TRUE; | |
| //read | |
| if (sk->cap_info->nf->read_sock_selected != sk) return GF_FALSE; | |
| if (sk->cap_info->nf->rt && (sk->cap_info->nf->pck_time > gf_sys_clock_high_res())) | |
| return GF_FALSE; | |
| return GF_TRUE; | |
| } | |
| #endif | |
| #ifdef GPAC_HAS_POLL | |
| if (sg->fds && sk->poll_idx) { | |
| GF_POLLFD *pfd = &sg->fds[sk->poll_idx-1]; | |
| //disconnected, consider ready to read/write | |
| if (pfd->revents & POLLHUP) | |
| return GF_TRUE; | |
| if ((mode!=GF_SK_SELECT_WRITE) && (pfd->revents & POLLIN)) | |
| return GF_TRUE; | |
| if ((mode!=GF_SK_SELECT_READ) && (pfd->revents & POLLOUT)) | |
| return GF_TRUE; | |
| // if (gf_sk_probe(sk) == GF_IP_CONNECTION_CLOSED) | |
| // return GF_TRUE; | |
| return GF_FALSE; | |
| } | |
| #endif | |
| if ((mode!=GF_SK_SELECT_WRITE) && FD_ISSET(sk->socket, &sg->rgroup)) | |
| return GF_TRUE; | |
| if ((mode!=GF_SK_SELECT_READ) && FD_ISSET(sk->socket, &sg->wgroup)) | |
| return GF_TRUE; | |
| return GF_FALSE; | |
| } | |
| //fetch nb bytes on a socket and fill the buffer from startFrom | |
| //length is the allocated size of the receiving buffer | |
| //BytesRead is the number of bytes read from the network | |
| GF_Err gf_sk_receive_internal(GF_Socket *sock, char *buffer, u32 length, u32 *BytesRead, Bool do_select) | |
| { | |
| s32 res; | |
| if (BytesRead) *BytesRead = 0; | |
| if (!sock) return GF_BAD_PARAM; | |
| #ifndef GPAC_DISABLE_NETCAP | |
| if (sock->cap_info) { | |
| if (!sock->cap_info->nf->read_socks) return GF_IP_NETWORK_EMPTY; | |
| GF_NetcapFilter *nf = sock->cap_info->nf; | |
| if (do_select && !(sock->flags & GF_SOCK_NON_BLOCKING)) { | |
| gf_netcap_load_pck(nf, gf_sys_clock_high_res() ); | |
| if (!nf->read_sock_selected) return nf->is_eos ? GF_IP_CONNECTION_CLOSED : GF_IP_NETWORK_EMPTY; | |
| if (nf->rt && (nf->pck_time > gf_sys_clock_high_res())) | |
| return GF_IP_NETWORK_EMPTY; | |
| } | |
| if (!buffer) return GF_OK; | |
| if (sock!=nf->read_sock_selected) return GF_IP_NETWORK_EMPTY; | |
| u32 to_read = length; | |
| if ((s32) to_read > nf->pck_len) to_read = nf->pck_len; | |
| u32 res = gf_bs_read_data(nf->cap_bs, buffer, to_read); | |
| if (nf->read_sock_selected->cap_info->patch_offset) { | |
| if (nf->read_sock_selected->cap_info->patch_offset-1<res) { | |
| buffer[nf->read_sock_selected->cap_info->patch_offset-1] = nf->read_sock_selected->cap_info->patch_val; | |
| nf->read_sock_selected->cap_info->patch_offset = 0; | |
| } else { | |
| nf->read_sock_selected->cap_info->patch_offset -= res; | |
| } | |
| } | |
| nf->pck_len -= res; | |
| if (!nf->pck_len) { | |
| nf->dst_port = 0; | |
| //do not load packet once done as this could lead to packet drop if the target socket is not yet created | |
| //gf_netcap_load_pck(nf, 0); | |
| } | |
| if (BytesRead) | |
| *BytesRead = res; | |
| if (!res) return GF_IP_NETWORK_EMPTY; | |
| return GF_OK; | |
| } | |
| #endif | |
| if (SOCKET_INVALID(sock->socket)) return GF_BAD_PARAM; | |
| if (do_select && !(sock->flags & GF_SOCK_NON_BLOCKING)) { | |
| //check read | |
| GF_Err e = poll_select(sock, GF_SK_SELECT_READ, sock->usec_wait, GF_FALSE); | |
| if (e) return e; | |
| } | |
| if (!buffer) return GF_OK; | |
| if ((sock->flags & GF_SOCK_HAS_PEER) || sock->dest_addr_len) { | |
| res = (s32) recvfrom(sock->socket, (char *) buffer, length, 0, (struct sockaddr *)&sock->dest_addr, &sock->dest_addr_len); | |
| } else { | |
| res = (s32) recv(sock->socket, (char *) buffer, length, 0); | |
| } | |
| if (!do_select && (res == 0)) | |
| return GF_IP_CONNECTION_CLOSED; | |
| if (res == SOCKET_ERROR) { | |
| res = LASTSOCKERROR; | |
| switch (res) { | |
| case EAGAIN: | |
| case EINTR: | |
| case ECONNREFUSED: | |
| return GF_IP_NETWORK_EMPTY; | |
| #if defined(WIN32) || defined(_WIN32_WCE) | |
| //may happen if no select | |
| case WSAEWOULDBLOCK: | |
| return GF_IP_NETWORK_EMPTY; | |
| #endif | |
| #ifndef __SYMBIAN32__ | |
| case EMSGSIZE: | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[socket] error reading: %s\n", gf_errno_str(LASTSOCKERROR))); | |
| return GF_OUT_OF_MEM; | |
| case ENOTCONN: | |
| case ECONNRESET: | |
| case ECONNABORTED: | |
| #if defined(WIN32) || defined(_WIN32_WCE) | |
| case WSAECONNRESET: | |
| #endif | |
| //log as debug, let higher level decide if this is an error or not | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[socket] error reading: %s\n", gf_errno_str(LASTSOCKERROR))); | |
| return GF_IP_CONNECTION_CLOSED; | |
| #endif | |
| default: | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[socket] error reading: %s\n", gf_errno_str(LASTSOCKERROR) )); | |
| return GF_IP_NETWORK_FAILURE; | |
| } | |
| } | |
| #ifndef GPAC_DISABLE_NETCAP | |
| if (netcap_filter_pck(sock, res, GF_FALSE)) { | |
| res = 0; | |
| } else if (sock->cap_info && sock->cap_info->patch_offset) { | |
| buffer[sock->cap_info->patch_offset-1] = sock->cap_info->patch_val; | |
| sock->cap_info->patch_offset = 0; | |
| } | |
| #endif | |
| if (!res) return GF_IP_NETWORK_EMPTY; | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[socket] read %u bytes\n", res )); | |
| if (BytesRead) | |
| *BytesRead = res; | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sk_receive(GF_Socket *sock, u8 *buffer, u32 length, u32 *BytesRead) | |
| { | |
| return gf_sk_receive_internal(sock, buffer, length, BytesRead, GF_TRUE); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sk_receive_no_select(GF_Socket *sock, u8 *buffer, u32 length, u32 *BytesRead) | |
| { | |
| return gf_sk_receive_internal(sock, buffer, length, BytesRead, GF_FALSE); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sk_listen(GF_Socket *sock, u32 MaxConnection) | |
| { | |
| s32 i; | |
| if (!sock || SOCKET_INVALID(sock->socket)) return GF_BAD_PARAM; | |
| #ifndef GPAC_DISABLE_NETCAP | |
| if (sock->cap_info && sock->cap_info->nf->cap_mode) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[socket] Listening on socket is not supported when using network capture\n")); | |
| return GF_NOT_SUPPORTED; | |
| } | |
| #endif | |
| if (!MaxConnection || (MaxConnection >= SOMAXCONN)) { | |
| MaxConnection = SOMAXCONN; | |
| } | |
| i = listen(sock->socket, MaxConnection); | |
| if (i == SOCKET_ERROR) | |
| return GF_IP_NETWORK_FAILURE; | |
| sock->flags |= GF_SOCK_IS_LISTENING; | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sk_accept(GF_Socket *sock, GF_Socket **newConnection) | |
| { | |
| u32 client_address_size; | |
| SOCKET sk; | |
| *newConnection = NULL; | |
| if (!sock || !(sock->flags & GF_SOCK_IS_LISTENING) ) return GF_BAD_PARAM; | |
| //check read | |
| GF_Err e = poll_select(sock, GF_SK_SELECT_READ, sock->usec_wait, GF_FALSE); | |
| if (e) return e; | |
| #ifdef GPAC_HAS_IPV6 | |
| client_address_size = sizeof(struct sockaddr_in6); | |
| #else | |
| client_address_size = sizeof(struct sockaddr_in); | |
| #endif | |
| sk = accept(sock->socket, (struct sockaddr *) &sock->dest_addr, &client_address_size); | |
| //we either have an error or we have no connections | |
| if (SOCKET_INVALID(sk)) { | |
| switch (LASTSOCKERROR) { | |
| case EAGAIN: | |
| case EINTR: | |
| return GF_IP_NETWORK_EMPTY; | |
| default: | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[socket] accept error: %s\n", gf_errno_str(LASTSOCKERROR))); | |
| return GF_IP_NETWORK_FAILURE; | |
| } | |
| } | |
| GF_SAFEALLOC((*newConnection), GF_Socket); | |
| (*newConnection)->socket = sk; | |
| (*newConnection)->flags = sock->flags & ~GF_SOCK_IS_LISTENING; | |
| (*newConnection)->usec_wait = sock->usec_wait; | |
| #ifdef GPAC_HAS_IPV6 | |
| memcpy( &(*newConnection)->dest_addr, &sock->dest_addr, client_address_size); | |
| memset(&sock->dest_addr, 0, sizeof(struct sockaddr_in6)); | |
| #else | |
| memcpy( &(*newConnection)->dest_addr, &sock->dest_addr, client_address_size); | |
| memset(&sock->dest_addr, 0, sizeof(struct sockaddr_in)); | |
| #endif | |
| #ifdef SO_NOSIGPIPE | |
| int value = 1; | |
| setsockopt((*newConnection)->socket, SOL_SOCKET, SO_NOSIGPIPE, SSO_CAST &value, sizeof(value)); | |
| #endif | |
| #if defined(WIN32) || defined(_WIN32_WCE) | |
| wsa_init++; | |
| #endif | |
| (*newConnection)->dest_addr_len = client_address_size; | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sk_get_local_info(GF_Socket *sock, u16 *Port, u32 *Familly) | |
| { | |
| #ifdef GPAC_HAS_IPV6 | |
| struct sockaddr_in6 the_add; | |
| #else | |
| struct sockaddr_in the_add; | |
| #endif | |
| u32 size; | |
| if (!sock || SOCKET_INVALID(sock->socket)) return GF_BAD_PARAM; | |
| if (Port) { | |
| #ifdef GPAC_HAS_IPV6 | |
| size = sizeof(struct sockaddr_in6); | |
| if (getsockname(sock->socket, (struct sockaddr *) &the_add, &size) == SOCKET_ERROR) return GF_IP_NETWORK_FAILURE; | |
| *Port = (u32) ntohs(the_add.sin6_port); | |
| #else | |
| size = sizeof(struct sockaddr_in); | |
| if (getsockname(sock->socket, (struct sockaddr *) &the_add, &size) == SOCKET_ERROR) return GF_IP_NETWORK_FAILURE; | |
| *Port = (u32) ntohs(the_add.sin_port); | |
| #endif | |
| } | |
| if (Familly) { | |
| /* size = 4; | |
| if (getsockopt(sock->socket, SOL_SOCKET, SO_TYPE, (char *) &fam, &size) == SOCKET_ERROR) | |
| return GF_IP_NETWORK_FAILURE; | |
| *Familly = fam; | |
| */ | |
| if (sock->flags & GF_SOCK_IS_TCP) *Familly = GF_SOCK_TYPE_TCP; | |
| else *Familly = GF_SOCK_TYPE_UDP; | |
| } | |
| return GF_OK; | |
| } | |
| //we have to do this for the server sockets as we use only one thread | |
| GF_EXPORT | |
| GF_Err gf_sk_server_mode(GF_Socket *sock, Bool serverOn) | |
| { | |
| u32 one; | |
| if (!sock || SOCKET_INVALID(sock->socket)) | |
| return GF_BAD_PARAM; | |
| if (!(sock->flags & GF_SOCK_IS_TCP)) { | |
| #if defined(IPV6_MTU_DISCOVER) || defined(IPV6_PMTUDISC_DO) || defined(IPV6_DONTFRAG) || defined(IP_MTU_DISCOVER) || defined(IP_DONTFRAG) | |
| int val; | |
| #endif | |
| #ifdef GPAC_HAS_IPV6 | |
| sock->dest_addr_len = sizeof(struct sockaddr_storage); | |
| #else | |
| sock->dest_addr_len = sizeof(struct sockaddr); | |
| #endif | |
| if (sock->flags & GF_SOCK_IS_IPV6) { | |
| #if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO) | |
| val = IPV6_PMTUDISC_DO; | |
| if (setsockopt(sock->socket, IPPROTO_IPV6, IPV6_MTU_DISCOVER, SSO_CAST &val, sizeof(val)) == -1) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[socket] Failed to set IPV6_MTU_DISCOVER: %s\n", gf_errno_str(LASTSOCKERROR) )); | |
| } | |
| #endif | |
| #ifdef IPV6_DONTFRAG | |
| val = 1; | |
| if (setsockopt(sock->socket, IPPROTO_IPV6, IPV6_DONTFRAG, SSO_CAST &val, sizeof(val) ) == -1) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[socket] Failed to set IPV6_DONTFRAG: %s\n", gf_errno_str(LASTSOCKERROR) )); | |
| } | |
| #endif | |
| } else { | |
| #ifdef IP_MTU_DISCOVER | |
| val = IP_PMTUDISC_DO; | |
| if (setsockopt(sock->socket, IPPROTO_IP, IP_MTU_DISCOVER, SSO_CAST &val, sizeof(val)) == -1) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[socket] Failed to set IP_MTU_DISCOVER: %s\n", gf_errno_str(LASTSOCKERROR) )); | |
| return GF_OK; | |
| } | |
| #endif | |
| #ifdef IP_DONTFRAG | |
| val = 1; | |
| if (setsockopt(sock->socket, IPPROTO_IP, IP_DONTFRAG, SSO_CAST &val, sizeof(val) ) == -1) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[socket] Failed to set IP_DONTFRAG: %s\n", gf_errno_str(LASTSOCKERROR) )); | |
| } | |
| #endif | |
| } | |
| return GF_OK; | |
| } | |
| one = serverOn ? 1 : 0; | |
| setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, SSO_CAST &one, sizeof(u32)); | |
| #ifndef __SYMBIAN32__ | |
| setsockopt(sock->socket, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(u32)); | |
| #endif | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sk_get_remote_address_port(GF_Socket *sock, char buf[GF_MAX_IP_NAME_LEN], u32 *port) | |
| { | |
| #ifdef GPAC_HAS_IPV6 | |
| char clienthost[NI_MAXHOST]; | |
| char servname[NI_MAXHOST]; | |
| struct sockaddr_in6 * addrptr = (struct sockaddr_in6 *)(&sock->dest_addr); | |
| if (!sock || SOCKET_INVALID(sock->socket)) return GF_BAD_PARAM; | |
| my_inet_ntop(AF_INET, addrptr, clienthost, NI_MAXHOST); | |
| gf_strlcpy(buf, clienthost, GF_MAX_IP_NAME_LEN); | |
| if (port) { | |
| *port = ntohs(addrptr->sin6_port); | |
| } | |
| if (getnameinfo((struct sockaddr *)addrptr, sock->dest_addr_len, clienthost, NI_MAXHOST, servname, NI_MAXHOST, NI_NUMERICHOST) == 0) { | |
| gf_strlcpy(buf, clienthost, GF_MAX_IP_NAME_LEN); | |
| } | |
| #else | |
| if (!sock || SOCKET_INVALID(sock->socket)) return GF_BAD_PARAM; | |
| gf_strlcpy(buf, inet_ntoa(sock->dest_addr.sin_addr), GF_MAX_IP_NAME_LEN); | |
| if (port) { | |
| *port = ntohs(sock->dest_addr.sin_port); | |
| } | |
| #endif | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sk_get_remote_address(GF_Socket *sock, char *buf) | |
| { | |
| return gf_sk_get_remote_address_port(sock, buf, NULL); | |
| } | |
| const u8 *gf_sk_get_address(GF_Socket *sock, u32 *addr_size) | |
| { | |
| if (!sock || !addr_size) return NULL; | |
| *addr_size = sock->dest_addr_len; | |
| return (const u8 *) &sock->dest_addr; | |
| } | |
| #if 0 //unused | |
| //send length bytes of a buffer | |
| GF_Err gf_sk_send_to(GF_Socket *sock, const char *buffer, u32 length, char *remoteHost, u16 remotePort) | |
| { | |
| u32 count, remote_add_len; | |
| s32 res; | |
| #ifdef GPAC_HAS_IPV6 | |
| struct sockaddr_storage remote_add; | |
| #else | |
| struct sockaddr_in remote_add; | |
| struct hostent *Host; | |
| #endif | |
| //the socket must be bound or connected | |
| if (!sock || SOCKET_INVALID(sock->socket)) return GF_BAD_PARAM; | |
| if (remoteHost && !remotePort) return GF_BAD_PARAM; | |
| //check write | |
| GF_Err e = poll_select(sock, GF_SK_SELECT_WRITE, sock->usec_wait, GF_FALSE); | |
| if (e) return e; | |
| /*setup the address*/ | |
| #ifdef GPAC_HAS_IPV6 | |
| remote_add.ss_family = AF_INET6; | |
| //if a remote host is specified, use it. Otherwise use the default host | |
| if (remoteHost) { | |
| //setup the address | |
| struct addrinfo *res = gf_sk_get_ipv6_addr(remoteHost, remotePort, AF_UNSPEC, 0, (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM); | |
| if (!res) return GF_IP_ADDRESS_NOT_FOUND; | |
| memcpy(&remote_add, res->ai_addr, res->ai_addrlen); | |
| remote_add_len = (u32) res->ai_addrlen; | |
| freeaddrinfo(res); | |
| } else { | |
| struct sockaddr_in6 *remotePtr = (struct sockaddr_in6 *)&remote_add; | |
| struct sockaddr_in6 * addrptr = (struct sockaddr_in6 *)(&sock->dest_addr); | |
| remotePtr->sin6_port = addrptr->sin6_port; | |
| remotePtr->sin6_addr = addrptr->sin6_addr; | |
| remote_add_len = sock->dest_addr_len; | |
| } | |
| #else | |
| remote_add_len = sizeof(struct sockaddr_in); | |
| remote_add.sin_family = AF_INET; | |
| //if a remote host is specified, use it. Otherwise use the default host | |
| if (remoteHost) { | |
| //setup the address | |
| remote_add.sin_port = htons(remotePort); | |
| //get the server IP | |
| Host = gf_gethostbyname(remoteHost); | |
| if (Host == NULL) return GF_IP_ADDRESS_NOT_FOUND; | |
| memcpy((char *) &remote_add.sin_addr, Host->h_addr_list[0], sizeof(u32)); | |
| } else { | |
| remote_add.sin_port = sock->dest_addr.sin_port; | |
| remote_add.sin_addr.s_addr = sock->dest_addr.sin_addr.s_addr; | |
| } | |
| #endif | |
| count = 0; | |
| while (count < length) { | |
| res = (s32) sendto(sock->socket, (char *) buffer+count, length - count, 0, (struct sockaddr *) &remote_add, remote_add_len); | |
| if (res == SOCKET_ERROR) { | |
| switch (LASTSOCKERROR) { | |
| case EAGAIN: | |
| case EINTR: | |
| return GF_IP_NETWORK_EMPTY; | |
| default: | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[socket] sendto error: %s\n", gf_errno_str(LASTSOCKERROR))); | |
| return GF_IP_NETWORK_FAILURE; | |
| } | |
| } | |
| count += res; | |
| } | |
| return GF_OK; | |
| } | |
| #endif | |
| GF_EXPORT | |
| GF_Err gf_sk_probe(GF_Socket *sock) | |
| { | |
| s32 res; | |
| u8 buffer[1]; | |
| if (!sock) return GF_BAD_PARAM; | |
| #ifndef GPAC_DISABLE_NETCAP | |
| if (sock->cap_info && sock->cap_info->nf->read_socks && sock->cap_info->nf->is_eos) return GF_IP_CONNECTION_CLOSED; | |
| #endif | |
| //check read - force using select at least for windows, for which poll returns nothing on connection abort | |
| GF_Err e = poll_select(sock, GF_SK_SELECT_READ, 100, GF_TRUE); | |
| if (e) return e; | |
| //peek message | |
| res = (s32) recv(sock->socket, buffer, 1, MSG_PEEK); | |
| if (res > 0) return GF_OK; | |
| #if 0 | |
| res = LASTSOCKERROR; | |
| switch (res) { | |
| case 0: | |
| case EAGAIN: | |
| case EINTR: | |
| return GF_IP_NETWORK_EMPTY; | |
| default: | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[socket] probe error: %s\n", gf_errno_str(res))); | |
| return GF_IP_CONNECTION_CLOSED; | |
| } | |
| #else | |
| return GF_IP_CONNECTION_CLOSED; | |
| #endif | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| char *gf_net_bump_ip_address(const char *in_ip, u32 increment) | |
| { | |
| if (!in_ip) return NULL; | |
| if (!gf_sk_is_multicast_address(in_ip)) return NULL; | |
| if (!increment) return gf_strdup(in_ip); | |
| u32 new_range=0; | |
| char *alloc_ip=NULL; | |
| if (!strnicmp(in_ip, "ff", 2)) { | |
| char *add_end = strstr(in_ip, "::"); | |
| if (!add_end) add_end = 1 + strrchr(in_ip, ':'); | |
| else add_end+=2; | |
| char *sep2 = strrchr(add_end, '/'); | |
| if (sep2) sep2[0] = 0; | |
| u32 inc; | |
| if (!add_end[0]) { | |
| inc = 0; | |
| gf_dynstrcat(&alloc_ip, in_ip, NULL); | |
| } else { | |
| char c = add_end[0]; | |
| add_end[0] = 0; | |
| gf_dynstrcat(&alloc_ip, in_ip, NULL); | |
| add_end[0] = c; | |
| inc = atoi(add_end); | |
| } | |
| new_range = inc + increment; | |
| char val[10]; | |
| sprintf(val, ".%u", new_range); | |
| add_end[0] = 0; | |
| gf_dynstrcat(&alloc_ip, val, NULL); | |
| if (sep2) { | |
| sep2[0] = '/'; | |
| gf_dynstrcat(&alloc_ip, sep2, NULL); | |
| } | |
| } else { | |
| char *sep = strrchr(in_ip, '.'); | |
| sep[0] =0; | |
| u32 inc = atoi(sep+1); | |
| new_range = inc + increment; | |
| char val[10]; | |
| sep[0] = 0; | |
| gf_dynstrcat(&alloc_ip, in_ip, NULL); | |
| sep[0] = '.'; | |
| sprintf(val, ".%u", new_range); | |
| gf_dynstrcat(&alloc_ip, val, NULL); | |
| } | |
| if (!alloc_ip) return NULL; | |
| if (new_range>0xFF) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[IP] Dumping IP address beyond 0xFF is not supported\n")); | |
| gf_free(alloc_ip); | |
| return NULL; | |
| } | |
| return alloc_ip; | |
| } | |
| #else | |
| //for ntoh/hton | |
| #include <arpa/inet.h> | |
| #endif //GPAC_DISABLE_NETWORK | |
| GF_EXPORT | |
| GF_Err gf_net_reload_netcap() | |
| { | |
| #if !defined(GPAC_DISABLE_NETWORK) && !defined(GPAC_DISABLE_NETCAP) | |
| u32 i, count = gf_list_count(netcap_filters); | |
| for (i=0; i<count; i++) { | |
| GF_NetcapFilter *nf = gf_list_get(netcap_filters, i); | |
| if (nf->cap_bs) gf_bs_del(nf->cap_bs); | |
| #ifdef GPAC_HAS_FD | |
| if (nf->fd>=0) close(nf->fd); | |
| #endif | |
| if (nf->file) gf_fclose(nf->file); | |
| gf_list_del(nf->read_socks); | |
| nf->read_socks = NULL; | |
| nf->nb_reloads++; | |
| nf->init_time = 0; | |
| nf->is_eos = GF_FALSE; | |
| nf->pck_start_offset = 0; | |
| nf->pck_len = 0; | |
| nf->pck_flags = 0; | |
| nf->pck_time = 0; | |
| nf->pcap_num_interfaces = 0; | |
| nf->pcapng_trail = 0; | |
| nf->pcap_trail = 0; | |
| nf->pck_idx_r = 0; | |
| nf->pck_idx_w = 0; | |
| nf->next_pck_range = 0; | |
| nf->next_rand = 0; | |
| nf->reorder_max_pck = 0; | |
| nf->reorder_nb_pck = 0; | |
| nf->delay_nb_pck = 0; | |
| nf->reorder_seek_pos = 0; | |
| nf->reorder_resume_pos = 0; | |
| GF_Err e = GF_OK; | |
| if (nf->dst) { | |
| e = gf_netcap_record(nf); | |
| } | |
| else if (nf->src) { | |
| e = gf_netcap_playback(nf); | |
| } | |
| if (e) return e; | |
| } | |
| #endif | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| u32 gf_htonl(u32 val) | |
| { | |
| return htonl(val); | |
| } | |
| GF_EXPORT | |
| u32 gf_ntohl(u32 val) | |
| { | |
| return ntohl(val); | |
| } | |
| GF_EXPORT | |
| u16 gf_htons(u16 val) | |
| { | |
| return htons(val); | |
| } | |
| GF_EXPORT | |
| u16 gf_ntohs(u16 val) | |
| { | |
| return ntohs(val); | |
| } | |
| GF_EXPORT | |
| const char *gf_errno_str(int errnoval) | |
| { | |
| return strerror(errnoval); | |
| } |