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
12extern int php_string_to_if_index(const char *val, unsigned *out TSRMLS_DC);
13
14#if HAVE_IPV6
15/* Sets addr by hostname, or by ip in string form (AF_INET6) */
16int php_set_inet6_addr(struct sockaddr_in6 *sin6, char *string, php_socket *php_sock TSRMLS_DC) /* {{{ */
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 TSRMLS_CC, 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 TSRMLS_CC, E_WARNING, "Host lookup failed: getaddrinfo() not available on this system");
58        return 0;
59#endif
60
61    }
62
63    if (scope++) {
64        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 && lval <= UINT_MAX) {
70                scope_id = lval;
71            }
72        } else {
73            php_string_to_if_index(scope, &scope_id TSRMLS_CC);
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)  */
85int php_set_inet_addr(struct sockaddr_in *sin, char *string, php_socket *php_sock TSRMLS_DC) /* {{{ */
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 (! (host_entry = 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 TSRMLS_CC, 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) */
115int php_set_inet46_addr(php_sockaddr_storage *ss, socklen_t *ss_len, char *string, php_socket *php_sock TSRMLS_DC) /* {{{ */
116{
117    if (php_sock->type == AF_INET) {
118        struct sockaddr_in t = {0};
119        if (php_set_inet_addr(&t, string, php_sock TSRMLS_CC)) {
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 TSRMLS_CC)) {
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 TSRMLS_CC, E_WARNING,
139            "IP address used in the context of an unexpected type of socket");
140    }
141    return 0;
142}
143