/* {{{ php_network_connect_socket_to_host */
php_socket_t php_network_connect_socket_to_host(const char *host, unsigned short port,
int socktype, int asynchronous, struct timeval *timeout, char **error_string,
- int *error_code
+ int *error_code, char *bindto, unsigned short bindport
TSRMLS_DC)
{
int num_addrs, n, fatal = 0;
if (sa) {
/* make a connection attempt */
+
+ if (bindto) {
+ struct sockaddr local_address;
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *in4 = (struct sockaddr_in*)&local_address;
+
+ in4->sin_family = sa->sa_family;
+ in4->sin_port = htons(bindport);
+ if (!inet_aton(bindto, &in4->sin_addr)) {
+ goto bad_ip;
+ }
+ bzero(&(in4->sin_zero), 8);
+ }
+#if HAVE_IPV6 && HAVE_INET_PTON
+ else { /* IPV6 */
+ struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&local_address;
+
+ in6->sin6_family = sa->sa_family;
+ in6->sin6_port = htons(bindport);
+ if (inet_pton(AF_INET6, bindto, &in6->sin6_addr) < 1) {
+ goto bad_ip;
+ }
+ }
+#endif
+ if (bind(sock, &local_address, sizeof(struct sockaddr))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to bind to '%s:%d', system said: %s", bindto, bindport, strerror(errno));
+ }
+ goto bind_done;
+bad_ip:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid IP Address: %s", bindto);
+ }
+bind_done:
n = php_network_connect_socket(sock, sa, socklen, asynchronous,
timeout ? &working_timeout : NULL,
error_string, error_code);
}
#endif
-static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno TSRMLS_DC)
+static inline char *parse_ip_address_ex(const char *str, int str_len, int *portno, int get_err, char **err TSRMLS_DC)
{
char *colon;
char *host = NULL;
#ifdef HAVE_IPV6
char *p;
- if (*(xparam->inputs.name) == '[') {
+ if (*(str) == '[') {
/* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */
- p = memchr(xparam->inputs.name + 1, ']', xparam->inputs.namelen - 2);
+ p = memchr(str + 1, ']', str_len - 2);
if (!p || *(p + 1) != ':') {
- if (xparam->want_errortext) {
- spprintf(&xparam->outputs.error_text, 0, "Failed to parse IPv6 address \"%s\"", xparam->inputs.name);
+ if (get_err) {
+ spprintf(err, 0, "Failed to parse IPv6 address \"%s\"", str);
}
return NULL;
}
*portno = atoi(p + 2);
- return estrndup(xparam->inputs.name + 1, p - xparam->inputs.name - 1);
+ return estrndup(str + 1, p - str - 1);
}
#endif
- colon = memchr(xparam->inputs.name, ':', xparam->inputs.namelen - 1);
+ colon = memchr(str, ':', str_len - 1);
if (colon) {
*portno = atoi(colon + 1);
- host = estrndup(xparam->inputs.name, colon - xparam->inputs.name);
+ host = estrndup(str, colon - str);
} else {
- if (xparam->want_errortext) {
- spprintf(&xparam->outputs.error_text, 0, "Failed to parse address \"%s\"", xparam->inputs.name);
+ if (get_err) {
+ spprintf(err, 0, "Failed to parse address \"%s\"", str);
}
return NULL;
}
return host;
}
+static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno TSRMLS_DC)
+{
+ return parse_ip_address_ex(xparam->inputs.name, xparam->inputs.namelen, portno, xparam->want_errortext, &xparam->outputs.error_text);
+}
+
static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
php_stream_xport_param *xparam TSRMLS_DC)
{
static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_t *sock,
php_stream_xport_param *xparam TSRMLS_DC)
{
- char *host = NULL;
- int portno;
+ char *host = NULL, *bindto = NULL;
+ int portno, bindport;
int err;
int ret;
+ zval **tmpzval = NULL;
#ifdef AF_UNIX
if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
return -1;
}
+ if (stream->context && php_stream_context_get_option(stream->context, "socket", "bindto", &tmpzval) == SUCCESS) {
+ if (Z_TYPE_PP(tmpzval) != IS_STRING) {
+ if (xparam->want_errortext) {
+ spprintf(&xparam->outputs.error_text, 0, "local_addr context option is not a string.");
+ }
+ return -1;
+ }
+ bindto = parse_ip_address_ex(Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval), &bindport, xparam->want_errortext, &xparam->outputs.error_text TSRMLS_CC);
+ }
+
/* Note: the test here for php_stream_udp_socket_ops is important, because we
* want the default to be TCP sockets so that the openssl extension can
* re-use this code. */
xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC,
xparam->inputs.timeout,
xparam->want_errortext ? &xparam->outputs.error_text : NULL,
- &err
+ &err,
+ bindto,
+ bindport
TSRMLS_CC);
ret = sock->socket == -1 ? -1 : 0;
if (host) {
efree(host);
}
+ if (bindto) {
+ efree(bindto);
+ }
#ifdef AF_UNIX
out: