xref: /PHP-7.4/ext/sockets/sockaddr_conv.c (revision adc95c51)
1 #include <php.h>
2 #include <php_network.h>
3 #include "php_sockets.h"
4 
5 #ifdef PHP_WIN32
6 #include "windows_common.h"
7 #else
8 #include <netdb.h>
9 #include <arpa/inet.h>
10 #endif
11 
12 extern int php_string_to_if_index(const char *val, unsigned *out);
13 
14 #if HAVE_IPV6
15 /* Sets addr by hostname, or by ip in string form (AF_INET6) */
php_set_inet6_addr(struct sockaddr_in6 * sin6,char * string,php_socket * php_sock)16 int php_set_inet6_addr(struct sockaddr_in6 *sin6, char *string, php_socket *php_sock) /* {{{ */
17 {
18 	struct in6_addr tmp;
19 #if HAVE_GETADDRINFO
20 	struct addrinfo hints;
21 	struct addrinfo *addrinfo = NULL;
22 #endif
23 	char *scope = strchr(string, '%');
24 
25 	if (inet_pton(AF_INET6, string, &tmp)) {
26 		memcpy(&(sin6->sin6_addr.s6_addr), &(tmp.s6_addr), sizeof(struct in6_addr));
27 	} else {
28 #if HAVE_GETADDRINFO
29 
30 		memset(&hints, 0, sizeof(struct addrinfo));
31 		hints.ai_family = AF_INET6;
32 #if HAVE_AI_V4MAPPED
33 		hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
34 #else
35 		hints.ai_flags = AI_ADDRCONFIG;
36 #endif
37 		getaddrinfo(string, NULL, &hints, &addrinfo);
38 		if (!addrinfo) {
39 #ifdef PHP_WIN32
40 			PHP_SOCKET_ERROR(php_sock, "Host lookup failed", WSAGetLastError());
41 #else
42 			PHP_SOCKET_ERROR(php_sock, "Host lookup failed", (-10000 - h_errno));
43 #endif
44 			return 0;
45 		}
46 		if (addrinfo->ai_family != PF_INET6 || addrinfo->ai_addrlen != sizeof(struct sockaddr_in6)) {
47 			php_error_docref(NULL, E_WARNING, "Host lookup failed: Non AF_INET6 domain returned on AF_INET6 socket");
48 			freeaddrinfo(addrinfo);
49 			return 0;
50 		}
51 
52 		memcpy(&(sin6->sin6_addr.s6_addr), ((struct sockaddr_in6*)(addrinfo->ai_addr))->sin6_addr.s6_addr, sizeof(struct in6_addr));
53 		freeaddrinfo(addrinfo);
54 
55 #else
56 		/* No IPv6 specific hostname resolution is available on this system? */
57 		php_error_docref(NULL, E_WARNING, "Host lookup failed: getaddrinfo() not available on this system");
58 		return 0;
59 #endif
60 
61 	}
62 
63 	if (scope++) {
64 		zend_long lval = 0;
65 		double dval = 0;
66 		unsigned scope_id = 0;
67 
68 		if (IS_LONG == is_numeric_string(scope, strlen(scope), &lval, &dval, 0)) {
69 			if (lval > 0 && (zend_ulong)lval <= UINT_MAX) {
70 				scope_id = lval;
71 			}
72 		} else {
73 			php_string_to_if_index(scope, &scope_id);
74 		}
75 
76 		sin6->sin6_scope_id = scope_id;
77 	}
78 
79 	return 1;
80 }
81 /* }}} */
82 #endif
83 
84 /* Sets addr by hostname, or by ip in string form (AF_INET)  */
php_set_inet_addr(struct sockaddr_in * sin,char * string,php_socket * php_sock)85 int php_set_inet_addr(struct sockaddr_in *sin, char *string, php_socket *php_sock) /* {{{ */
86 {
87 	struct in_addr tmp;
88 	struct hostent *host_entry;
89 
90 	if (inet_aton(string, &tmp)) {
91 		sin->sin_addr.s_addr = tmp.s_addr;
92 	} else {
93 		if (strlen(string) > MAXFQDNLEN || ! (host_entry = php_network_gethostbyname(string))) {
94 			/* Note: < -10000 indicates a host lookup error */
95 #ifdef PHP_WIN32
96 			PHP_SOCKET_ERROR(php_sock, "Host lookup failed", WSAGetLastError());
97 #else
98 			PHP_SOCKET_ERROR(php_sock, "Host lookup failed", (-10000 - h_errno));
99 #endif
100 			return 0;
101 		}
102 		if (host_entry->h_addrtype != AF_INET) {
103 			php_error_docref(NULL, E_WARNING, "Host lookup failed: Non AF_INET domain returned on AF_INET socket");
104 			return 0;
105 		}
106 		memcpy(&(sin->sin_addr.s_addr), host_entry->h_addr_list[0], host_entry->h_length);
107 	}
108 
109 	return 1;
110 }
111 /* }}} */
112 
113 /* Sets addr by hostname or by ip in string form (AF_INET or AF_INET6,
114  * depending on the socket) */
php_set_inet46_addr(php_sockaddr_storage * ss,socklen_t * ss_len,char * string,php_socket * php_sock)115 int php_set_inet46_addr(php_sockaddr_storage *ss, socklen_t *ss_len, char *string, php_socket *php_sock) /* {{{ */
116 {
117 	if (php_sock->type == AF_INET) {
118 		struct sockaddr_in t = {0};
119 		if (php_set_inet_addr(&t, string, php_sock)) {
120 			memcpy(ss, &t, sizeof t);
121 			ss->ss_family = AF_INET;
122 			*ss_len = sizeof(t);
123 			return 1;
124 		}
125 	}
126 #if HAVE_IPV6
127 	else if (php_sock->type == AF_INET6) {
128 		struct sockaddr_in6 t = {0};
129 		if (php_set_inet6_addr(&t, string, php_sock)) {
130 			memcpy(ss, &t, sizeof t);
131 			ss->ss_family = AF_INET6;
132 			*ss_len = sizeof(t);
133 			return 1;
134 		}
135 	}
136 #endif
137 	else {
138 		php_error_docref(NULL, E_WARNING,
139 			"IP address used in the context of an unexpected type of socket");
140 	}
141 	return 0;
142 }
143