char *get_ip_by_socket(zbx_sock_t *s) { ZBX_SOCKADDR sa; ZBX_SOCKLEN_T sz; static char buffer[64]; *buffer = '\0'; sz = sizeof(sa); if (ZBX_TCP_ERROR == getpeername(s->socket, (struct sockaddr*)&sa, &sz)) { zbx_set_tcp_strerror("connection rejected, getpeername() failed: %s", strerror_from_system(zbx_sock_last_error())); return buffer; } #if defined(HAVE_IPV6) if (0 != getnameinfo((struct sockaddr*)&sa, sizeof(sa), buffer, sizeof(buffer), NULL, 0, NI_NUMERICHOST)) { zbx_set_tcp_strerror("connection rejected, getnameinfo() failed: %s", strerror_from_system(zbx_sock_last_error())); } #else zbx_snprintf(buffer, sizeof(buffer), "%s", inet_ntoa(sa.sin_addr)); #endif return buffer; }
static int zbx_tcp_start() { WSADATA sockInfo; switch (WSAStartup(MAKEWORD(2, 2), &sockInfo)) { case WSASYSNOTREADY: zbx_set_tcp_strerror("Underlying network subsystem is not ready for network communication."); return FAIL; case WSAVERNOTSUPPORTED: zbx_set_tcp_strerror("The version of Windows Sockets support requested is not provided."); return FAIL; case WSAEINPROGRESS: zbx_set_tcp_strerror("A blocking Windows Sockets 1.1 operation is in progress."); return FAIL; case WSAEPROCLIM: zbx_set_tcp_strerror("Limit on the number of tasks supported by the Windows Sockets implementation has been reached."); return FAIL; case WSAEFAULT: zbx_set_tcp_strerror("The lpWSAData is not a valid pointer."); return FAIL; } return SUCCEED; }
char *get_ip_by_socket(zbx_sock_t *s) { ZBX_SOCKADDR sa; ZBX_SOCKLEN_T sz = sizeof(sa); static char host[64]; char *error_message = NULL; if (ZBX_TCP_ERROR == getpeername(s->socket, (struct sockaddr*)&sa, &sz)) { error_message = strerror_from_system(zbx_sock_last_error()); zbx_set_tcp_strerror("connection rejected, getpeername() failed: %s", error_message); goto out; } #if defined(HAVE_IPV6) if (0 != zbx_getnameinfo((struct sockaddr *)&sa, host, sizeof(host), NULL, 0, NI_NUMERICHOST)) { error_message = strerror_from_system(zbx_sock_last_error()); zbx_set_tcp_strerror("connection rejected, getnameinfo() failed: %s", error_message); } #else zbx_snprintf(host, sizeof(host), "%s", inet_ntoa(sa.sin_addr)); #endif out: if (NULL != error_message) { zabbix_log(LOG_LEVEL_WARNING, "Cannot get socket IP address: %s", error_message); strscpy(host, "unknown IP"); } return host; }
char *get_ip_by_socket(zbx_sock_t *s) { #if defined(HAVE_IPV6) struct sockaddr_storage sa; #else ZBX_SOCKADDR sa; #endif socklen_t sz; static char buffer[64]; zabbix_log( LOG_LEVEL_DEBUG, "In get_ip_by_socket()"); *buffer = '\0'; sz = sizeof(sa); if (ZBX_TCP_ERROR == getpeername(s->socket, (struct sockaddr*)&sa, &sz)) { zbx_set_tcp_strerror("Connection rejected. getpeername failed [%s]", strerror_from_system(zbx_sock_last_error())); return buffer; } #if defined(HAVE_IPV6) if (0 != getnameinfo((struct sockaddr*)&sa, sizeof(sa), buffer, sizeof(buffer), NULL, 0, NI_NUMERICHOST)) { zbx_set_tcp_strerror("Connection rejected. getnameinfo failed [%s] ", strerror_from_system(zbx_sock_last_error())); } #else zbx_snprintf(buffer, sizeof(buffer), "%s", inet_ntoa(sa.sin_addr)); #endif return buffer; }
int zbx_tcp_accept(zbx_sock_t *s) { struct sockaddr_storage serv_addr; fd_set sock_set; ZBX_SOCKET accepted_socket; socklen_t nlen; int i, n = 0; zbx_tcp_unaccept(s); FD_ZERO(&sock_set); /* For connection orientated protocols, we will handle the packets comprising a connection collectively. For datagram protocols, we have to handle each datagram individually. Check to see if we have any sockets remaining to be served from previous time through this loop. If not, call select() to wait for a connection request or a datagram to arrive. */ for(i = 0; i < s->num_socks; i++) { #if !defined(_WINDOWS) if(s->sockets[i] > n) n = s->sockets[i]; #endif if(FD_ISSET(s->sockets[i], &sock_set)) break; } if(i == s->num_socks) { for (i = 0; i < s->num_socks; i++) FD_SET(s->sockets[i], &sock_set); if(select(n + 1, &sock_set, 0, 0, 0) == ZBX_TCP_ERROR) { zbx_set_tcp_strerror("select() failed with error %d: %s", zbx_sock_last_error(), strerror_from_system(zbx_sock_last_error())); return FAIL; } } for (i = 0; i < s->num_socks; i++) { if (FD_ISSET(s->sockets[i], &sock_set)) { FD_CLR(s->sockets[i], &sock_set); break; } } /* Since this socket was returned by the select(), we know we have a connection waiting and that this accept() won't block.*/ nlen = sizeof(serv_addr); if((accepted_socket = (ZBX_SOCKET)accept(s->sockets[i], (struct sockaddr *)&serv_addr, &nlen)) == ZBX_SOCK_ERROR) { zbx_set_tcp_strerror("accept() failed with error %d: %s", zbx_sock_last_error(), strerror_from_system(zbx_sock_last_error())); return FAIL; } s->socket2 = s->socket; /* remember main socket */ s->socket = accepted_socket; /* replace socket to accepted */ s->accepted = 1; return SUCCEED; }
int zbx_tcp_listen( zbx_sock_t *s, const char *listen_ip, unsigned short listen_port ) { ZBX_SOCKADDR serv_addr; int on, res = FAIL; ZBX_TCP_START(); zbx_tcp_clean(s); if(ZBX_SOCK_ERROR == (s->socket = socket(AF_INET,SOCK_STREAM,0))) { zbx_set_tcp_strerror("Cannot create socket [%s:%u] [%s]", listen_ip, listen_port, strerror_from_system(zbx_sock_last_error())); goto out; } /* Enable address reuse */ /* This is to immediately use the address even if it is in TIME_WAIT state */ /* http://www-128.ibm.com/developerworks/linux/library/l-sockpit/index.html */ on = 1; if( -1 == setsockopt(s->socket, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on) )) { zbx_set_tcp_strerror("Cannot setsockopt SO_REUSEADDR [%s]", strerror_from_system(zbx_sock_last_error())); } /* Create socket Fill in local address structure */ memset(&serv_addr, 0, sizeof(ZBX_SOCKADDR)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = listen_ip ? inet_addr(listen_ip) : htonl(INADDR_ANY); serv_addr.sin_port = htons((unsigned short)listen_port); /* Bind socket */ if (ZBX_SOCK_ERROR == bind(s->socket,(struct sockaddr *)&serv_addr,sizeof(ZBX_SOCKADDR)) ) { zbx_set_tcp_strerror("Cannot bind to port %u for server %s. Error [%s]. Another zabbix_agentd already running ?", listen_port, listen_ip ? listen_ip : "[ANY]", strerror_from_system(zbx_sock_last_error())); goto out; } if( ZBX_SOCK_ERROR == listen(s->socket, SOMAXCONN) ) { zbx_set_tcp_strerror("Listen failed. [%s]", strerror_from_system(zbx_sock_last_error())); goto out; } res = SUCCEED; out: return res; }
int zbx_tcp_connect(zbx_sock_t *s, const char *source_ip, const char *ip, unsigned short port, int timeout) { ZBX_SOCKADDR servaddr_in, source_addr; struct hostent *hp; ZBX_TCP_START(); zbx_tcp_clean(s); if (NULL == (hp = zbx_gethost(ip))) return FAIL; servaddr_in.sin_family = AF_INET; servaddr_in.sin_addr.s_addr = ((struct in_addr *)(hp->h_addr))->s_addr; servaddr_in.sin_port = htons(port); if (ZBX_SOCK_ERROR == (s->socket = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0))) { zbx_set_tcp_strerror("Cannot create socket [%s:%d] [%s]", ip, port, strerror_from_system(zbx_sock_last_error())); return FAIL; } #if !defined(_WINDOWS) && defined(HAVE_FCNTL_H) && !SOCK_CLOEXEC fcntl(s->socket, F_SETFD, FD_CLOEXEC); #endif if (NULL != source_ip) { source_addr.sin_family = AF_INET; source_addr.sin_addr.s_addr = inet_addr(source_ip); source_addr.sin_port = 0; if (ZBX_TCP_ERROR == bind(s->socket, (struct sockaddr *)&source_addr, sizeof(ZBX_SOCKADDR))) { zbx_set_tcp_strerror("bind() failed with error %d: %s\n", zbx_sock_last_error(), strerror_from_system(zbx_sock_last_error())); return FAIL; } } if (0 != timeout) zbx_tcp_timeout_set(s, timeout); if (ZBX_TCP_ERROR == connect(s->socket, (struct sockaddr *)&servaddr_in, sizeof(ZBX_SOCKADDR))) { zbx_set_tcp_strerror("Cannot connect to [%s:%d] [%s]", ip, port, strerror_from_system(zbx_sock_last_error())); zbx_tcp_close(s); return FAIL; } return SUCCEED; }
int zbx_tcp_send_ext(zbx_sock_t *s, const char *data, unsigned char flags, int timeout) { zbx_uint64_t len64; ssize_t i = 0, written = 0; int ret = SUCCEED; ZBX_TCP_START(); if (0 != timeout) zbx_tcp_timeout_set(s, timeout); if( flags & ZBX_TCP_NEW_PROTOCOL ) { /* Write header */ if( ZBX_TCP_ERROR == ZBX_TCP_WRITE(s->socket, ZBX_TCP_HEADER, ZBX_TCP_HEADER_LEN)) { zbx_set_tcp_strerror("ZBX_TCP_WRITE() failed [%s]", strerror_from_system(zbx_sock_last_error())); ret = FAIL; goto cleanup; } len64 = (zbx_uint64_t)strlen(data); len64 = zbx_htole_uint64(len64); /* Write data length */ if( ZBX_TCP_ERROR == ZBX_TCP_WRITE(s->socket, (char *) &len64, sizeof(len64)) ) { zbx_set_tcp_strerror("ZBX_TCP_WRITE() failed [%s]", strerror_from_system(zbx_sock_last_error())); ret = FAIL; goto cleanup; } } while(written < (ssize_t)strlen(data)) { if( ZBX_TCP_ERROR == (i = ZBX_TCP_WRITE(s->socket, data+written,(int)(strlen(data)-written))) ) { zbx_set_tcp_strerror("ZBX_TCP_WRITE() failed [%s]", strerror_from_system(zbx_sock_last_error())); ret = FAIL; goto cleanup; } written += i; } cleanup: if (0 != timeout) zbx_tcp_timeout_cleanup(s); return ret; }
/****************************************************************************** * * * Function: zbx_tcp_timeout_set * * * * Purpose: set timeout for socket operations * * * * Parameters: s - [IN] socket descriptor * * timeout - [IN] timeout, in seconds * * * * Author: Alexander Vladishev * * * ******************************************************************************/ static void zbx_tcp_timeout_set(zbx_sock_t *s, int timeout) { s->timeout = timeout; #if defined(_WINDOWS) timeout *= 1000; if (ZBX_TCP_ERROR == setsockopt(s->socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(timeout))) zbx_set_tcp_strerror("setsockopt() failed: %s", strerror_from_system(zbx_sock_last_error())); if (ZBX_TCP_ERROR == setsockopt(s->socket, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(timeout))) zbx_set_tcp_strerror("setsockopt() failed: %s", strerror_from_system(zbx_sock_last_error())); #else alarm(timeout); #endif }
/****************************************************************************** * * * Function: zbx_tcp_accept * * * * Purpose: permits an incoming connection attempt on a socket * * * * Return value: SUCCEED - success * * FAIL - an error occurred * * * * Author: Eugene Grigorjev, Aleksandrs Saveljevs * * * ******************************************************************************/ int zbx_tcp_accept(zbx_sock_t *s) { ZBX_SOCKADDR serv_addr; fd_set sock_set; ZBX_SOCKET accepted_socket; ZBX_SOCKLEN_T nlen; int i, n = 0; zbx_tcp_unaccept(s); FD_ZERO(&sock_set); for (i = 0; i < s->num_socks; i++) { FD_SET(s->sockets[i], &sock_set); #if !defined(_WINDOWS) if (s->sockets[i] > n) n = s->sockets[i]; #endif } if (ZBX_TCP_ERROR == select(n + 1, &sock_set, NULL, NULL, NULL)) { zbx_set_tcp_strerror("select() failed: %s", strerror_from_system(zbx_sock_last_error())); return FAIL; } for (i = 0; i < s->num_socks; i++) { if (FD_ISSET(s->sockets[i], &sock_set)) break; } /* Since this socket was returned by select(), we know we have */ /* a connection waiting and that this accept() will not block. */ nlen = sizeof(serv_addr); if (ZBX_SOCK_ERROR == (accepted_socket = (ZBX_SOCKET)accept(s->sockets[i], (struct sockaddr *)&serv_addr, &nlen))) { zbx_set_tcp_strerror("accept() failed: %s", strerror_from_system(zbx_sock_last_error())); return FAIL; } s->socket_orig = s->socket; /* remember main socket */ s->socket = accepted_socket; /* replace socket to accepted */ s->accepted = 1; return SUCCEED; }
int zbx_tcp_connect(zbx_sock_t *s, const char *source_ip, const char *ip, unsigned short port, int timeout) { ZBX_SOCKADDR servaddr_in, source_addr; struct hostent *hp; ZBX_TCP_START(); zbx_tcp_clean(s); if (NULL == (hp = gethostbyname(ip))) { #if defined(_WINDOWS) zbx_set_tcp_strerror("gethostbyname() failed for '%s': %s", ip, strerror_from_system(WSAGetLastError())); #elif defined(HAVE_HSTRERROR) zbx_set_tcp_strerror("gethostbyname() failed for '%s': [%d] %s", ip, h_errno, hstrerror(h_errno)); #else zbx_set_tcp_strerror("gethostbyname() failed for '%s': [%d]", ip, h_errno); #endif return FAIL; } servaddr_in.sin_family = AF_INET; servaddr_in.sin_addr.s_addr = ((struct in_addr *)(hp->h_addr))->s_addr; servaddr_in.sin_port = htons(port); if (ZBX_SOCK_ERROR == (s->socket = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0))) { zbx_set_tcp_strerror("cannot create socket [[%s]:%d]: %s", ip, port, strerror_from_system(zbx_sock_last_error())); return FAIL; } #if !defined(_WINDOWS) && !SOCK_CLOEXEC fcntl(s->socket, F_SETFD, FD_CLOEXEC); #endif if (NULL != source_ip) { source_addr.sin_family = AF_INET; source_addr.sin_addr.s_addr = inet_addr(source_ip); source_addr.sin_port = 0; if (ZBX_TCP_ERROR == bind(s->socket, (struct sockaddr *)&source_addr, sizeof(source_addr))) { zbx_set_tcp_strerror("bind() failed: %s", strerror_from_system(zbx_sock_last_error())); return FAIL; } } if (0 != timeout) zbx_tcp_timeout_set(s, timeout); if (ZBX_TCP_ERROR == connect(s->socket, (struct sockaddr *)&servaddr_in, sizeof(servaddr_in))) { zbx_set_tcp_strerror("cannot connect to [[%s]:%d]: %s", ip, port, strerror_from_system(zbx_sock_last_error())); zbx_tcp_close(s); return FAIL; } return SUCCEED; }
static int zbx_tcp_start() { WSADATA sockInfo; int ret; if (0 != (ret = WSAStartup(MAKEWORD(2, 2), &sockInfo))) { zbx_set_tcp_strerror("WSAStartup() failed: %s", strerror_from_system(ret)); return FAIL; } return SUCCEED; }
/****************************************************************************** * * * Function: zbx_gethost * * * * Purpose: retrieve 'hostent' by host name and IP * * * * Parameters: * * * * Return value: hostent or NULL - an error occurred * * * * Author: Eugene Grigorjev * * * * Comments: * * * ******************************************************************************/ struct hostent *zbx_gethost(const char *hostname) { unsigned int addr; struct hostent *host; assert(hostname); host = gethostbyname(hostname); if(host) return host; addr = inet_addr(hostname); host = gethostbyaddr((char *)&addr, 4, AF_INET); if(host) return host; zbx_set_tcp_strerror("gethost() failed for address '%s' [%s]", hostname, strerror_from_system(zbx_sock_last_error())); return (struct hostent *)NULL; }
int zbx_tcp_accept(zbx_sock_t *s) { ZBX_SOCKADDR serv_addr; ZBX_SOCKET accepted_socket; socklen_t nlen; nlen = sizeof(serv_addr); zbx_tcp_unaccept(s); if(ZBX_TCP_ERROR == (accepted_socket = (ZBX_SOCKET)accept(s->socket, (struct sockaddr *)&serv_addr, &nlen))) { zbx_set_tcp_strerror("accept() failed [%s]", strerror_from_system(zbx_sock_last_error())); return FAIL; } s->socket2 = s->socket; /* remember main socket */ s->socket = accepted_socket; /* replace socket to accepted */ s->accepted = 1; return SUCCEED; }
int zbx_tcp_listen(zbx_sock_t *s, const char *listen_ip, unsigned short listen_port) { ZBX_SOCKADDR serv_addr; char *ip, *ips, *delim; int i, on, ret = FAIL; ZBX_TCP_START(); zbx_tcp_clean(s); ip = ips = (NULL == listen_ip ? NULL : strdup(listen_ip)); while (1) { delim = (NULL == ip ? NULL : strchr(ip, ',')); if (NULL != delim) *delim = '\0'; if (NULL != ip && FAIL == is_ip4(ip)) { zbx_set_tcp_strerror("incorrect IPv4 address [%s]", ip); goto out; } if (ZBX_SOCKET_COUNT == s->num_socks) { zbx_set_tcp_strerror("not enough space for socket [[%s]:%hu]", ip ? ip : "-", listen_port); goto out; } if (ZBX_SOCK_ERROR == (s->sockets[s->num_socks] = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0))) { zbx_set_tcp_strerror("socket() for [[%s]:%hu] failed: %s", ip ? ip : "-", listen_port, strerror_from_system(zbx_sock_last_error())); goto out; } #if !defined(_WINDOWS) && !SOCK_CLOEXEC fcntl(s->sockets[s->num_socks], F_SETFD, FD_CLOEXEC); #endif /* Enable address reuse */ /* This is to immediately use the address even if it is in TIME_WAIT state */ /* http://www-128.ibm.com/developerworks/linux/library/l-sockpit/index.html */ on = 1; if (ZBX_TCP_ERROR == setsockopt(s->sockets[s->num_socks], SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on))) { zbx_set_tcp_strerror("setsockopt() for [[%s]:%hu] failed: %s", ip ? ip : "-", listen_port, strerror_from_system(zbx_sock_last_error())); } memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = NULL != ip ? inet_addr(ip) : htonl(INADDR_ANY); serv_addr.sin_port = htons((unsigned short)listen_port); if (ZBX_TCP_ERROR == bind(s->sockets[s->num_socks], (struct sockaddr *)&serv_addr, sizeof(serv_addr))) { zbx_set_tcp_strerror("bind() for [[%s]:%hu] failed: %s", ip ? ip : "-", listen_port, strerror_from_system(zbx_sock_last_error())); zbx_sock_close(s->sockets[s->num_socks]); goto out; } if (ZBX_TCP_ERROR == listen(s->sockets[s->num_socks], SOMAXCONN)) { zbx_set_tcp_strerror("listen() for [[%s]:%hu] failed: %s", ip ? ip : "-", listen_port, strerror_from_system(zbx_sock_last_error())); zbx_sock_close(s->sockets[s->num_socks]); goto out; } s->num_socks++; if (NULL == ip || NULL == delim) break; *delim = ','; ip = delim + 1; } if (0 == s->num_socks) { zbx_set_tcp_strerror("zbx_tcp_listen() fatal error: unable to serve on any address [[%s]:%hu]", listen_ip ? listen_ip : "-", listen_port); goto out; } ret = SUCCEED; out: if (NULL != ips) zbx_free(ips); if (SUCCEED != ret) { for (i = 0; i < s->num_socks; i++) zbx_sock_close(s->sockets[i]); } return ret; }
int zbx_tcp_listen(zbx_sock_t *s, const char *listen_ip, unsigned short listen_port) { struct addrinfo hints, *ai = NULL, *current_ai; char port[8], *ip, *ips, *delim; int i, err, on, ret = FAIL; ZBX_TCP_START(); zbx_tcp_clean(s); memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; zbx_snprintf(port, sizeof(port), "%hu", listen_port); ip = ips = (NULL == listen_ip ? NULL : strdup(listen_ip)); while (1) { delim = (NULL == ip ? NULL : strchr(ip, ',')); if (NULL != delim) *delim = '\0'; if (0 != (err = getaddrinfo(ip, port, &hints, &ai))) { zbx_set_tcp_strerror("cannot resolve address [[%s]:%s]: [%d] %s", ip ? ip : "-", port, err, gai_strerror(err)); goto out; } for (current_ai = ai; NULL != current_ai; current_ai = current_ai->ai_next) { if (ZBX_SOCKET_COUNT == s->num_socks) { zbx_set_tcp_strerror("not enough space for socket [[%s]:%s]", ip ? ip : "-", port); goto out; } if (PF_INET != current_ai->ai_family && PF_INET6 != current_ai->ai_family) continue; if (ZBX_SOCK_ERROR == (s->sockets[s->num_socks] = socket(current_ai->ai_family, current_ai->ai_socktype | SOCK_CLOEXEC, current_ai->ai_protocol))) { zbx_set_tcp_strerror("socket() for [[%s]:%s] failed: %s", ip ? ip : "-", port, strerror_from_system(zbx_sock_last_error())); #ifdef _WINDOWS if (WSAEAFNOSUPPORT == zbx_sock_last_error()) #else if (EAFNOSUPPORT == zbx_sock_last_error()) #endif continue; else goto out; } #if !defined(_WINDOWS) && !SOCK_CLOEXEC fcntl(s->sockets[s->num_socks], F_SETFD, FD_CLOEXEC); #endif /* enable address reuse */ /* this is to immediately use the address even if it is in TIME_WAIT state */ /* http://www-128.ibm.com/developerworks/linux/library/l-sockpit/index.html */ on = 1; if (ZBX_TCP_ERROR == setsockopt(s->sockets[s->num_socks], SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on))) { zbx_set_tcp_strerror("setsockopt() with SO_REUSEADDR for [[%s]:%s] failed: %s", ip ? ip : "-", port, strerror_from_system(zbx_sock_last_error())); } #if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) if (PF_INET6 == current_ai->ai_family && ZBX_TCP_ERROR == setsockopt(s->sockets[s->num_socks], IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on))) { zbx_set_tcp_strerror("setsockopt() with IPV6_V6ONLY for [[%s]:%s] failed: %s", ip ? ip : "-", port, strerror_from_system(zbx_sock_last_error())); } #endif if (ZBX_TCP_ERROR == bind(s->sockets[s->num_socks], current_ai->ai_addr, current_ai->ai_addrlen)) { zbx_set_tcp_strerror("bind() for [[%s]:%s] failed: %s", ip ? ip : "-", port, strerror_from_system(zbx_sock_last_error())); zbx_sock_close(s->sockets[s->num_socks]); #ifdef _WINDOWS if (WSAEADDRINUSE == zbx_sock_last_error()) #else if (EADDRINUSE == zbx_sock_last_error()) #endif continue; else goto out; } if (ZBX_TCP_ERROR == listen(s->sockets[s->num_socks], SOMAXCONN)) { zbx_set_tcp_strerror("listen() for [[%s]:%s] failed: %s", ip ? ip : "-", port, strerror_from_system(zbx_sock_last_error())); zbx_sock_close(s->sockets[s->num_socks]); goto out; } s->num_socks++; } if (NULL != ai) { freeaddrinfo(ai); ai = NULL; } if (NULL == ip || NULL == delim) break; *delim = ','; ip = delim + 1; } if (0 == s->num_socks) { zbx_set_tcp_strerror("zbx_tcp_listen() fatal error: unable to serve on any address [[%s]:%hu]", listen_ip ? listen_ip : "-", listen_port); goto out; } ret = SUCCEED; out: if (NULL != ips) zbx_free(ips); if (NULL != ai) freeaddrinfo(ai); if (SUCCEED != ret) { for (i = 0; i < s->num_socks; i++) zbx_sock_close(s->sockets[i]); } return ret; }
int zbx_tcp_connect(zbx_sock_t *s, const char *source_ip, const char *ip, unsigned short port, int timeout) { int ret = FAIL; struct addrinfo *ai = NULL, hints; struct addrinfo *ai_bind = NULL; char service[8]; ZBX_TCP_START(); zbx_tcp_clean(s); zbx_snprintf(service, sizeof(service), "%d", port); memset(&hints, 0x00, sizeof(struct addrinfo)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (0 != getaddrinfo(ip, service, &hints, &ai)) { zbx_set_tcp_strerror("cannot resolve [%s]", ip); goto out; } if (ZBX_SOCK_ERROR == (s->socket = socket(ai->ai_family, ai->ai_socktype | SOCK_CLOEXEC, ai->ai_protocol))) { zbx_set_tcp_strerror("cannot create socket [[%s]:%d]: %s", ip, port, strerror_from_system(zbx_sock_last_error())); goto out; } #if !defined(_WINDOWS) && !SOCK_CLOEXEC fcntl(s->socket, F_SETFD, FD_CLOEXEC); #endif if (NULL != source_ip) { memset(&hints, 0x00, sizeof(struct addrinfo)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICHOST; if (0 != getaddrinfo(source_ip, NULL, &hints, &ai_bind)) { zbx_set_tcp_strerror("invalid source IP address [%s]", source_ip); goto out; } if (ZBX_TCP_ERROR == bind(s->socket, ai_bind->ai_addr, ai_bind->ai_addrlen)) { zbx_set_tcp_strerror("bind() failed: %s", strerror_from_system(zbx_sock_last_error())); goto out; } } if (0 != timeout) zbx_tcp_timeout_set(s, timeout); if (ZBX_TCP_ERROR == connect(s->socket, ai->ai_addr, ai->ai_addrlen)) { zbx_set_tcp_strerror("cannot connect to [[%s]:%d]: %s", ip, port, strerror_from_system(zbx_sock_last_error())); zbx_tcp_close(s); goto out; } ret = SUCCEED; out: if (NULL != ai) freeaddrinfo(ai); if (NULL != ai_bind) freeaddrinfo(ai_bind); return ret; }
/****************************************************************************** * * * Function: zbx_tcp_check_security * * * * Purpose: check if connection initiator is in list of IP addresses * * * * Parameters: s - socket descriptor * * ip_list - comma-delimited list of IP addresses * * allow_if_empty - allow connection if no IP given * * * * Return value: SUCCEED - connection allowed * * FAIL - connection is not allowed * * * * Author: Alexei Vladishev, Dmitry Borovikov * * * * Comments: standard, compatible and IPv4-mapped addresses are treated * * the same: 127.0.0.1 == ::127.0.0.1 == ::ffff:127.0.0.1 * * * ******************************************************************************/ int zbx_tcp_check_security(zbx_sock_t *s, const char *ip_list, int allow_if_empty) { #if defined(HAVE_IPV6) struct addrinfo hints, *ai = NULL, *current_ai; #else struct hostent *hp; int i; #endif ZBX_SOCKADDR name; ZBX_SOCKLEN_T nlen; char tmp[MAX_STRING_LEN], *start = NULL, *end = NULL; if (1 == allow_if_empty && (NULL == ip_list || '\0' == *ip_list)) return SUCCEED; nlen = sizeof(name); if (ZBX_TCP_ERROR == getpeername(s->socket, (struct sockaddr *)&name, &nlen)) { zbx_set_tcp_strerror("connection rejected, getpeername() failed: %s", strerror_from_system(zbx_sock_last_error())); return FAIL; } else { strscpy(tmp, ip_list); for (start = tmp; '\0' != *start;) { if (NULL != (end = strchr(start, ','))) *end = '\0'; /* allow IP addresses or DNS names for authorization */ #if defined(HAVE_IPV6) memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; if (0 == getaddrinfo(start, NULL, &hints, &ai)) { for (current_ai = ai; NULL != current_ai; current_ai = current_ai->ai_next) { if (SUCCEED == zbx_ip_cmp(current_ai, name)) { freeaddrinfo(ai); return SUCCEED; } } freeaddrinfo(ai); } #else if (NULL != (hp = gethostbyname(start))) { for (i = 0; NULL != hp->h_addr_list[i]; i++) { if (name.sin_addr.s_addr == ((struct in_addr *)hp->h_addr_list[i])->s_addr) return SUCCEED; } } #endif /* HAVE_IPV6 */ if (NULL != end) { *end = ','; start = end + 1; } else break; } if (NULL != end) *end = ','; } #if defined(HAVE_IPV6) if (0 == zbx_getnameinfo((struct sockaddr *)&name, tmp, sizeof(tmp), NULL, 0, NI_NUMERICHOST)) zbx_set_tcp_strerror("connection from \"%s\" rejected, allowed hosts: \"%s\"", tmp, ip_list); else zbx_set_tcp_strerror("connection rejected, allowed hosts: \"%s\"", ip_list); #else zbx_set_tcp_strerror("connection from \"%s\" rejected, allowed hosts: \"%s\"", inet_ntoa(name.sin_addr), ip_list); #endif return FAIL; }
int zbx_tcp_connect(zbx_sock_t *s, const char *source_ip, const char *ip, unsigned short port, int timeout ) { ZBX_SOCKADDR servaddr_in, source_addr; struct hostent *hp; ZBX_TCP_START(); zbx_tcp_clean(s); if (NULL == (hp = zbx_gethost(ip))) { zbx_set_tcp_strerror("Cannot resolve [%s]", ip); return FAIL; } servaddr_in.sin_family = AF_INET; servaddr_in.sin_addr.s_addr = ((struct in_addr *)(hp->h_addr))->s_addr; servaddr_in.sin_port = htons(port); if (ZBX_SOCK_ERROR == (s->socket = socket(AF_INET,SOCK_STREAM,0))) { zbx_set_tcp_strerror("Cannot create socket [%s:%d] [%s]", ip, port ,strerror_from_system(zbx_sock_last_error())); return FAIL; } if (NULL != source_ip) { source_addr.sin_family = AF_INET; source_addr.sin_addr.s_addr = inet_addr(source_ip); source_addr.sin_port = 0; if (ZBX_SOCK_ERROR == bind(s->socket, (struct sockaddr *)&source_addr, sizeof(ZBX_SOCKADDR)) ) { zbx_set_tcp_strerror("bind() failed with error %d: %s\n", zbx_sock_last_error(), strerror_from_system(zbx_sock_last_error())); return FAIL; } } if (0 != timeout) { s->timeout = timeout; #if defined(_WINDOWS) timeout *= 1000; if (setsockopt(s->socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(timeout)) == ZBX_TCP_ERROR) zbx_set_tcp_strerror("setsockopt() failed with error %d: %s", zbx_sock_last_error(), strerror_from_system(zbx_sock_last_error())); if (setsockopt(s->socket, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(timeout)) == ZBX_TCP_ERROR) zbx_set_tcp_strerror("setsockopt() failed with error %d: %s", zbx_sock_last_error(), strerror_from_system(zbx_sock_last_error())); #else alarm(timeout); #endif } if (ZBX_TCP_ERROR == connect(s->socket,(struct sockaddr *)&servaddr_in,sizeof(ZBX_SOCKADDR))) { zbx_set_tcp_strerror("Cannot connect to [%s:%d] [%s]", ip, port, strerror_from_system(zbx_sock_last_error())); zbx_tcp_close(s); return FAIL; } return SUCCEED; }
/****************************************************************************** * * * Function: check_security * * * * Purpose: check if connection initiator is in list of IP addresses * * * * Parameters: sockfd - socket descriptor * * ip_list - comma-delimited list of IP addresses * * allow_if_empty - allow connection if no IP given * * * * Return value: SUCCEED - connection allowed * * FAIL - connection is not allowed * * * * Author: Alexei Vladishev, Dmitry Borovikov * * * * Comments: standard, compatible and IPv4-mapped addresses are treated * * the same: 127.0.0.1 == ::127.0.0.1 == ::ffff:127.0.0.1 * * * ******************************************************************************/ int zbx_tcp_check_security(zbx_sock_t *s, const char *ip_list, int allow_if_empty) { #if defined(HAVE_IPV6) struct addrinfo hints, *ai = NULL; /* Network Byte Order is ensured */ unsigned char ipv4_cmp_mask[12] = {0}; /* IPv4-Compatible, the first 96 bits are zeros */ unsigned char ipv4_mpd_mask[12] = {0,0,0,0,0,0,0,0,0,0,255,255}; /* IPv4-Mapped, the first 80 bits are zeros, 16 next - ones */ #else struct hostent *hp; char *sip; int i[4], j[4]; #endif ZBX_SOCKADDR name; ZBX_SOCKLEN_T nlen; char tmp[MAX_STRING_LEN], sname[MAX_STRING_LEN], *start = NULL, *end = NULL; if (1 == allow_if_empty && (NULL == ip_list || '\0' == *ip_list)) return SUCCEED; nlen = sizeof(name); if (ZBX_TCP_ERROR == getpeername(s->socket, (struct sockaddr *)&name, &nlen)) { zbx_set_tcp_strerror("connection rejected, getpeername() failed: %s", strerror_from_system(zbx_sock_last_error())); return FAIL; } else { #if !defined(HAVE_IPV6) zbx_strlcpy(sname, inet_ntoa(name.sin_addr), sizeof(sname)); if (4 != sscanf(sname, "%d.%d.%d.%d", &i[0], &i[1], &i[2], &i[3])) return FAIL; #endif strscpy(tmp,ip_list); for (start = tmp; '\0' != *start;) { if (NULL != (end = strchr(start, ','))) *end = '\0'; /* allow IP addresses or DNS names for authorization */ #if defined(HAVE_IPV6) memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; if (0 == getaddrinfo(start, NULL, &hints, &ai)) { #ifdef HAVE_SOCKADDR_STORAGE_SS_FAMILY if (ai->ai_family == name.ss_family) #else if (ai->ai_family == name.__ss_family) #endif { switch (ai->ai_family) { case AF_INET : if (((struct sockaddr_in*)&name)->sin_addr.s_addr == ((struct sockaddr_in*)ai->ai_addr)->sin_addr.s_addr) { freeaddrinfo(ai); return SUCCEED; } break; case AF_INET6 : if (0 == memcmp(((struct sockaddr_in6*)&name)->sin6_addr.s6_addr, ((struct sockaddr_in6*)ai->ai_addr)->sin6_addr.s6_addr, sizeof(struct in6_addr))) { freeaddrinfo(ai); return SUCCEED; } break; } } else { switch (ai->ai_family) { case AF_INET : /* incoming AF_INET6, must see whether it is comp or mapped */ if((0 == memcmp(((struct sockaddr_in6*)&name)->sin6_addr.s6_addr, ipv4_cmp_mask, 12) || 0 == memcmp(((struct sockaddr_in6*)&name)->sin6_addr.s6_addr, ipv4_mpd_mask, 12)) && 0 == memcmp(&((struct sockaddr_in6*)&name)->sin6_addr.s6_addr[12], (unsigned char*)&((struct sockaddr_in*)ai->ai_addr)->sin_addr.s_addr, 4)) { freeaddrinfo(ai); return SUCCEED; } break; case AF_INET6 : /* incoming AF_INET, must see whether the given is comp or mapped */ if((0 == memcmp(((struct sockaddr_in6*)ai->ai_addr)->sin6_addr.s6_addr, ipv4_cmp_mask, 12) || 0 == memcmp(((struct sockaddr_in6*)ai->ai_addr)->sin6_addr.s6_addr, ipv4_mpd_mask, 12)) && 0 == memcmp(&((struct sockaddr_in6*)ai->ai_addr)->sin6_addr.s6_addr[12], (unsigned char*)&((struct sockaddr_in*)&name)->sin_addr.s_addr, 4)) { freeaddrinfo(ai); return SUCCEED; } break; } } freeaddrinfo(ai); } #else if (NULL != (hp = gethostbyname(start))) { sip = inet_ntoa(*((struct in_addr *)hp->h_addr)); if (4 == sscanf(sip, "%d.%d.%d.%d", &j[0], &j[1], &j[2], &j[3]) && i[0] == j[0] && i[1] == j[1] && i[2] == j[2] && i[3] == j[3]) { return SUCCEED; } } #endif /* HAVE_IPV6 */ if (NULL != end) { *end = ','; start = end + 1; } else break; } if (NULL != end) *end = ','; } #if defined(HAVE_IPV6) if (0 == getnameinfo((struct sockaddr *)&name, sizeof(name), sname, sizeof(sname), NULL, 0, NI_NUMERICHOST)) zbx_set_tcp_strerror("Connection from [%s] rejected. Allowed server is [%s].", sname, ip_list); else zbx_set_tcp_strerror("Connection rejected. Allowed server is [%s].", ip_list); #else zbx_set_tcp_strerror("Connection from [%s] rejected. Allowed server is [%s].", sname, ip_list); #endif return FAIL; }
int zbx_tcp_listen( zbx_sock_t *s, const char *listen_ip, unsigned short listen_port ) { struct addrinfo hints, *ai = NULL, *current_ai; char port[MAX_STRING_LEN]; int e, on, ret = FAIL; ZBX_TCP_START(); zbx_tcp_clean(s); memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; zbx_snprintf(port, sizeof(port), "%d", listen_port); if(0 != (e = getaddrinfo(listen_ip, port, &hints, &ai))) { zbx_set_tcp_strerror("Cannot resolve address [[%s]:%u], error %d: %s", listen_ip, listen_port, e, gai_strerror(e)); goto out; } for(s->num_socks = 0, current_ai = ai; current_ai != NULL; current_ai = current_ai->ai_next) { if(s->num_socks == FD_SETSIZE) { break; } /* This example only supports PF_INET and PF_INET6. */ if((current_ai->ai_family != PF_INET) && (current_ai->ai_family != PF_INET6)) continue; if((s->sockets[s->num_socks] = socket(current_ai->ai_family, current_ai->ai_socktype, current_ai->ai_protocol)) == ZBX_SOCK_ERROR) { zbx_set_tcp_strerror("socket() failed with error %d: %s", zbx_sock_last_error(), strerror_from_system(zbx_sock_last_error())); continue; } /* Enable address reuse */ /* This is to immediately use the address even if it is in TIME_WAIT state */ /* http://www-128.ibm.com/developerworks/linux/library/l-sockpit/index.html */ on = 1; if(setsockopt(s->sockets[s->num_socks], SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)) == ZBX_TCP_ERROR) { zbx_set_tcp_strerror("setsockopt() failed with error %d: %s", zbx_sock_last_error(), strerror_from_system(zbx_sock_last_error())); } /* Create socket Fill in local address structure */ if(bind(s->sockets[s->num_socks], current_ai->ai_addr, current_ai->ai_addrlen) == ZBX_TCP_ERROR) { zbx_set_tcp_strerror("bind() failed with error %d: %s", zbx_sock_last_error(), strerror_from_system(zbx_sock_last_error())); zbx_sock_close(s->sockets[s->num_socks]); continue; } if(ZBX_SOCK_ERROR == listen(s->sockets[s->num_socks], SOMAXCONN) ) { zbx_set_tcp_strerror("listen() failed with error %d: %s", zbx_sock_last_error(), strerror_from_system(zbx_sock_last_error())); zbx_sock_close(s->sockets[s->num_socks]); continue; } s->num_socks++; } if(s->num_socks == 0) { zbx_set_tcp_strerror("zbx_tcp_listen() Fatal error: unable to serve on any address. [[%s]:%u]", listen_ip, listen_port); goto out; } ret = SUCCEED; out: if (NULL != ai) freeaddrinfo(ai); return ret; }
int zbx_tcp_connect(zbx_sock_t *s, const char *source_ip, const char *ip, unsigned short port, int timeout ) { int ret = FAIL; struct addrinfo *ai = NULL, hints; struct addrinfo *ai_bind = NULL; char service[MAX_STRING_LEN]; ZBX_TCP_START(); zbx_tcp_clean(s); zbx_snprintf(service, sizeof(service), "%d", port); memset(&hints, 0x00, sizeof(struct addrinfo)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (0 != getaddrinfo(ip, service, &hints, &ai)) { zbx_set_tcp_strerror("Cannot resolve [%s]", ip); goto out; } if (ZBX_SOCK_ERROR == (s->socket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol))) { zbx_set_tcp_strerror("Cannot create socket [%s]:%d [%s]", ip, port ,strerror_from_system(zbx_sock_last_error())); goto out; } if (NULL != source_ip) { memset(&hints, 0x00, sizeof(struct addrinfo)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICHOST; if (0 != getaddrinfo(source_ip, NULL, &hints, &ai_bind)) { zbx_set_tcp_strerror("Invalid source IP address [%s]\n", source_ip); goto out; } if (ZBX_TCP_ERROR == bind(s->socket, ai_bind->ai_addr, ai_bind->ai_addrlen)) { zbx_set_tcp_strerror("bind() failed with error %d: %s\n", zbx_sock_last_error(), strerror_from_system(zbx_sock_last_error())); goto out; } } if (0 != timeout) { s->timeout = timeout; #if defined(_WINDOWS) timeout *= 1000; if (setsockopt(s->socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(timeout)) == ZBX_TCP_ERROR) zbx_set_tcp_strerror("setsockopt() failed with error %d: %s", zbx_sock_last_error(), strerror_from_system(zbx_sock_last_error())); if (setsockopt(s->socket, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(timeout)) == ZBX_TCP_ERROR) zbx_set_tcp_strerror("setsockopt() failed with error %d: %s", zbx_sock_last_error(), strerror_from_system(zbx_sock_last_error())); #else alarm(timeout); #endif } if (ZBX_TCP_ERROR == connect(s->socket, ai->ai_addr, ai->ai_addrlen)) { zbx_set_tcp_strerror("*** Cannot connect to [%s]:%d [%s]", ip, port, strerror_from_system(zbx_sock_last_error())); zbx_tcp_close(s); goto out; } ret = SUCCEED; out: if (NULL != ai) freeaddrinfo(ai); if (NULL != ai_bind) freeaddrinfo(ai_bind); return ret; }
/****************************************************************************** * * * Function: zbx_tcp_recv * * * * Purpose: receive data * * * * Parameters: * * * * Return value: SUCCEED - success * * FAIL - an error occurred * * * * Author: Eugene Grigorjev * * * * Comments: * * * ******************************************************************************/ int zbx_tcp_recv_ext(zbx_sock_t *s, char **data, unsigned char flags, int timeout) { #define ZBX_BUF_LEN ZBX_STAT_BUF_LEN*8 ssize_t nbytes, left; ssize_t read_bytes; int allocated, offset; int ret = SUCCEED; zbx_uint64_t expected_len; ZBX_TCP_START(); if (0 != timeout) zbx_tcp_timeout_set(s, timeout); zbx_free(s->buf_dyn); memset(s->buf_stat, 0, sizeof(s->buf_stat)); *data = s->buf_stat; read_bytes = 0; s->buf_type = ZBX_BUF_TYPE_STAT; left = ZBX_TCP_HEADER_LEN; nbytes = ZBX_TCP_READ(s->socket, s->buf_stat, left); if( ZBX_TCP_HEADER_LEN == nbytes && 0 == strncmp(s->buf_stat, ZBX_TCP_HEADER, ZBX_TCP_HEADER_LEN) ) { left = sizeof(zbx_uint64_t); nbytes = ZBX_TCP_READ(s->socket, (void *)&expected_len, left); expected_len = zbx_letoh_uint64(expected_len); /* The rest was already cleared */ memset(s->buf_stat, 0, ZBX_TCP_HEADER_LEN); flags |= ZBX_TCP_READ_UNTIL_CLOSE; } else if( ZBX_TCP_ERROR != nbytes ) { read_bytes = nbytes; expected_len = 16*1024*1024; } if( ZBX_TCP_ERROR != nbytes ) { if( flags & ZBX_TCP_READ_UNTIL_CLOSE ) { if(nbytes == 0) goto cleanup; } else { if(nbytes < left) goto cleanup; } left = sizeof(s->buf_stat) - read_bytes - 1; /* fill static buffer */ if ( s->buf_stat[ read_bytes - 1 ] != '\n' ) /* Don't try to read from an empty socket. */ { while( read_bytes < expected_len && left > 0 && ZBX_TCP_ERROR != (nbytes = ZBX_TCP_READ( s->socket, s->buf_stat + read_bytes, left))) { read_bytes += nbytes; if( flags & ZBX_TCP_READ_UNTIL_CLOSE ) { if(nbytes == 0) break; } else { if(nbytes < left) break; } left -= nbytes; } } s->buf_stat[read_bytes] = '\0'; if( (sizeof(s->buf_stat) - 1) == read_bytes) /* static buffer is full */ { allocated = ZBX_BUF_LEN; s->buf_type = ZBX_BUF_TYPE_DYN; s->buf_dyn = zbx_malloc(s->buf_dyn, allocated); memset(s->buf_dyn, 0, allocated); memcpy(s->buf_dyn, s->buf_stat, sizeof(s->buf_stat)); offset = read_bytes; /* fill dynamic buffer */ while( read_bytes < expected_len && ZBX_TCP_ERROR != (nbytes = ZBX_TCP_READ(s->socket, s->buf_stat, sizeof(s->buf_stat)-1)) ) { s->buf_stat[nbytes] = '\0'; zbx_snprintf_alloc(&(s->buf_dyn), &allocated, &offset, sizeof(s->buf_stat), "%s", s->buf_stat); read_bytes += nbytes; if( flags & ZBX_TCP_READ_UNTIL_CLOSE ) { if(nbytes == 0) break; } else { if(nbytes < sizeof(s->buf_stat) - 1) break; } } *data = s->buf_dyn; } } if( ZBX_TCP_ERROR == nbytes ) { zbx_set_tcp_strerror("ZBX_TCP_READ() failed [%s]", strerror_from_system(zbx_sock_last_error())); ret = FAIL; } cleanup: if (0 != timeout) zbx_tcp_timeout_cleanup(s); return ret; }
/****************************************************************************** * * * Function: zbx_tcp_recv_ext * * * * Purpose: receive data * * * * Return value: number of bytes received - success, * * FAIL - an error occurred * * * * Author: Eugene Grigorjev * * * ******************************************************************************/ ssize_t zbx_tcp_recv_ext(zbx_sock_t *s, char **data, unsigned char flags, int timeout) { #define ZBX_BUF_LEN (ZBX_STAT_BUF_LEN * 8) ssize_t nbytes, left, total_bytes; size_t allocated, offset, read_bytes; zbx_uint64_t expected_len; ZBX_TCP_START(); if (0 != timeout) zbx_tcp_timeout_set(s, timeout); zbx_free(s->buf_dyn); total_bytes = 0; read_bytes = 0; s->buf_type = ZBX_BUF_TYPE_STAT; *data = s->buf_stat; left = ZBX_TCP_HEADER_LEN; if (ZBX_TCP_ERROR == (nbytes = ZBX_TCP_READ(s->socket, s->buf_stat, left))) goto out; if (ZBX_TCP_HEADER_LEN == nbytes && 0 == strncmp(s->buf_stat, ZBX_TCP_HEADER, ZBX_TCP_HEADER_LEN)) { total_bytes += nbytes; left = sizeof(zbx_uint64_t); if (left != (nbytes = ZBX_TCP_READ(s->socket, (void *)&expected_len, left))) { total_bytes = FAIL; goto out; } expected_len = zbx_letoh_uint64(expected_len); if (ZBX_MAX_RECV_DATA_SIZE < expected_len) { zabbix_log(LOG_LEVEL_WARNING, "Message size " ZBX_FS_UI64 " from %s" " exceeds the maximum size " ZBX_FS_UI64 " bytes. Message ignored.", expected_len, get_ip_by_socket(s), (zbx_uint64_t)ZBX_MAX_RECV_DATA_SIZE); total_bytes = FAIL; goto cleanup; } flags |= ZBX_TCP_READ_UNTIL_CLOSE; } else { read_bytes = nbytes; expected_len = 16 * ZBX_MEBIBYTE; } s->buf_stat[read_bytes] = '\0'; if (0 != (flags & ZBX_TCP_READ_UNTIL_CLOSE)) { if (0 == nbytes) goto cleanup; } else { if (nbytes < left) goto cleanup; } left = sizeof(s->buf_stat) - read_bytes - 1; /* check for an empty socket if exactly ZBX_TCP_HEADER_LEN bytes (without a header) were sent */ if (0 == read_bytes || '\n' != s->buf_stat[read_bytes - 1]) /* requests to passive agents end with '\n' */ { /* fill static buffer */ while (read_bytes < expected_len && 0 < left && ZBX_TCP_ERROR != (nbytes = ZBX_TCP_READ(s->socket, s->buf_stat + read_bytes, left))) { read_bytes += nbytes; if (0 != (flags & ZBX_TCP_READ_UNTIL_CLOSE)) { if (0 == nbytes) break; } else { if (nbytes < left) /* should we stop reading? */ { /* XML protocol? */ if (0 == strncmp(s->buf_stat, "<req>", sizeof("<req>") - 1)) { /* closing tag received in the last 10 bytes? */ s->buf_stat[read_bytes] = '\0'; if (NULL != strstr(s->buf_stat + read_bytes - (10 > read_bytes ? read_bytes : 10), "</req>")) break; } else break; } } left -= nbytes; } } s->buf_stat[read_bytes] = '\0'; if (sizeof(s->buf_stat) - 1 == read_bytes) /* static buffer is full */ { allocated = ZBX_BUF_LEN; s->buf_type = ZBX_BUF_TYPE_DYN; s->buf_dyn = zbx_malloc(s->buf_dyn, allocated); memcpy(s->buf_dyn, s->buf_stat, sizeof(s->buf_stat)); offset = read_bytes; /* fill dynamic buffer */ while (read_bytes < expected_len && ZBX_TCP_ERROR != (nbytes = ZBX_TCP_READ(s->socket, s->buf_stat, sizeof(s->buf_stat)))) { zbx_strncpy_alloc(&s->buf_dyn, &allocated, &offset, s->buf_stat, nbytes); read_bytes += nbytes; if (0 != (flags & ZBX_TCP_READ_UNTIL_CLOSE)) { if (0 == nbytes) break; } else { if ((size_t)nbytes < sizeof(s->buf_stat) - 1) /* should we stop reading? */ { /* XML protocol? */ if (0 == strncmp(s->buf_dyn, "<req>", sizeof("<req>") - 1)) { /* closing tag received in the last 10 bytes? */ if (NULL != strstr(s->buf_dyn + read_bytes - 10, "</req>")) break; } else break; } } } *data = s->buf_dyn; } out: if (ZBX_TCP_ERROR == nbytes) { zbx_set_tcp_strerror("ZBX_TCP_READ() failed: %s", strerror_from_system(zbx_sock_last_error())); total_bytes = FAIL; } cleanup: if (0 != timeout) zbx_tcp_timeout_cleanup(s); if (FAIL != total_bytes) total_bytes += read_bytes; return total_bytes; }
int zbx_tcp_check_security( zbx_sock_t *s, const char *ip_list, int allow_if_empty ) { #if defined(HAVE_IPV6) struct sockaddr_storage name; struct addrinfo hints, *ai = NULL; #else ZBX_SOCKADDR name; struct hostent *hp; char *sip; int i[4], j[4]; #endif socklen_t nlen; char tmp[MAX_STRING_LEN], sname[MAX_STRING_LEN], *start = NULL, *end = NULL, c = '\0'; zabbix_log( LOG_LEVEL_DEBUG, "In check_security()"); if( (1 == allow_if_empty) && ( !ip_list || !*ip_list ) ) { return SUCCEED; } nlen = sizeof(name); if( ZBX_TCP_ERROR == getpeername(s->socket, (struct sockaddr*)&name, &nlen)) { zbx_set_tcp_strerror("Connection rejected. Getpeername failed [%s]", strerror_from_system(zbx_sock_last_error())); return FAIL; } else { #if !defined(HAVE_IPV6) zbx_strlcpy(sname, inet_ntoa(name.sin_addr), sizeof(sname)); if(sscanf(sname, "%d.%d.%d.%d", &i[0], &i[1], &i[2], &i[3]) != 4) { return FAIL; } #endif /*HAVE_IPV6*/ strscpy(tmp,ip_list); for(start = tmp; start[0] != '\0';) { end = strchr(start, ','); if(end != NULL) { c = end[0]; end[0] = '\0'; } /* Allow IP addresses or DNS names for authorization */ #if defined(HAVE_IPV6) memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; if(0 == getaddrinfo(start, NULL, &hints, &ai)) { if(ai->ai_family == name.ss_family) { switch(ai->ai_family) { case AF_INET : if(((struct sockaddr_in*)&name)->sin_addr.s_addr == ((struct sockaddr_in*)ai->ai_addr)->sin_addr.s_addr) { freeaddrinfo(ai); return SUCCEED; } case AF_INET6 : if(0 == memcmp(((struct sockaddr_in6*)&name)->sin6_addr.s6_addr, ((struct sockaddr_in6*)ai->ai_addr)->sin6_addr.s6_addr, sizeof(struct in6_addr))) { freeaddrinfo(ai); return SUCCEED; } } } freeaddrinfo(ai); } #else if( 0 != (hp = zbx_gethost(start))) { sip = inet_ntoa(*((struct in_addr *)hp->h_addr)); if(sscanf(sip, "%d.%d.%d.%d", &j[0], &j[1], &j[2], &j[3]) == 4) { if(i[0] == j[0] && i[1] == j[1] && i[2] == j[2] && i[3] == j[3]) { return SUCCEED; } } } #endif /*HAVE_IPV6*/ if(end != NULL) { end[0] = c; start = end + 1; } else { break; } } if(end != NULL) { end[0] = c; } } #if defined(HAVE_IPV6) if(0 == getnameinfo((struct sockaddr*)&name, sizeof(name), sname, sizeof(sname), NULL, 0, NI_NUMERICHOST)) { zbx_set_tcp_strerror("Connection from [%s] rejected. Allowed server is [%s] ",sname, ip_list); } else { zbx_set_tcp_strerror("Connection rejected. Allowed server is [%s] ", ip_list); } #else zbx_set_tcp_strerror("Connection from [%s] rejected. Allowed server is [%s] ",sname, ip_list); #endif /*HAVE_IPV6*/ return FAIL; }