Asked  1 Year ago    Answers:  5   Viewed   9 times

I need to send emails from PHPMailer using proxies IP addresses, I know that to do so, I need to use the fsockopen function so I can connect to the SMTP account, I also know that if I have to connect to the proxy I have to use the fsockopen function again. But using it fsockopen inside another fsockopen is not doable.

I have transparent proxy and require no authentication. I need to send this to a distant SMTP server of an external Email Service Provider.

The code I have tried :

<?php

    //SMTP params
    $server      = 'smtp.espdomain.com';
    $server_port = '25';
    $username = 'smtp_login';
    $password = 'smtp_pass';

    //Proxy
    $proxy      = '1.1.1.1';
    $proxy_port = 1111;

    //Open connection
    $socket = fsockopen($proxy, $proxy_port);

    //Send command to proxy
    fputs($socket, "CONNECT $server:$server_port HTTP/1.0rnHost: $proxyrnrn");
    fgets($socket, 334);

    //SMTP authorization  
    fputs($socket, "AUTH LOGINrn");
    fgets($socket, 334);

    fputs($socket, base64_encode($username)."rn");
    fgets($socket, 334);

    fputs($socket, base64_encode($password)."rn");
    $output = fgets($socket, 235);

    fputs($socket, "HELO $server rn"); 
    $output = fgets($socket, 515);

?>

And it's not working I'm not sure why?

Could socat commands help in this situation or is there any solution or alternative solution to achieve that?

 Answers

2

I finally found the solution using socat, Kindly follow these steps :

  1. First of all, you'll need to install socat on the server, you can do that simply using the command below :

    yum install socat
    
  2. Then run the following socat command that will bind PROXY_IP:PORT with HOST_ESP:PORT :

    socat TCP4-LISTEN:proxy_port,bind=proxy_IP,fork,su=nobody TCP4:host:port,bind=proxy_IP
    
  3. Then instead of making a send to the ESP through HOST_ESP:PORT you could just make it using PROXY_IP:PORT and socat will do the redirection automatically towards HOST_ESP:PORT using the output of PROXY_IP:PORT.

Hope this helps.

Thursday, April 1, 2021
 
IcedAnt
 
4

Wrong approach.

PHPMailer is not a mail server, which is what you're asking it to be. SMTP is a verbose, chatty protocol that's prone to delays and slow throughput, and is absolutely unsuited to sending interactively during a typical web page submission (which is what the question BlackHatSamurai linked to is probably doing). Many get away with doing precisely that, but don't be fooled into thinking it's a good solution, and definitely don't try to implement an MTA yourself.

The gmail example you linked to is using SMTP to a remote server, which will always be slower than submitting locally. If you're submitting via sendmail (or mail() - it's basically the same thing) to a local server and it's taking more than about 0.1 sec, you're doing something very wrong. Even SMTP to localhost won't take much longer, and sending to a nearby smarthost is unlikely to be too slow either.

Trying to background things using threads is a massive can of worms that is exactly not the way to go about this - whatever you achieve that way would be terrible compared to a proper mail server. Just don't do it.

The right way to do this is to install a local mail server, and submit your messages to it with PHPMailer. This way is very fast (hundreds of messages per second), and you have to do precisely nothing to make it work because that's how PHPMailer works by default.

The mail server will then do what it's supposed to do - queue your message, deal with connection problems, delivery deferrals, bounces and everything else you've not considered.

Thursday, April 1, 2021
 
nomie
 
2

As far as I know there is no default option to set a SOCKS or HTTP proxy for fsockopen or stream_socket_client (we could create a context and set a proxy in HTTP options, but that doesn't apply to stream_socket_client). However we can establish a connection manually.

Connecting to HTTP proxies is quite simple:

  • The client connects to the proxy server and submits a CONNECT request.
  • The server responds 200 if the request is accepted.
  • The server then proxies all requests between the client and destination host.

function connect_to_http_proxy($host, $port, $destination) {
    $fp = fsockopen($host, $port, $errno, $errstr);
    if ($errno == 0) {
        $connect = "CONNECT $destination HTTP/1.1rnrn";
        fwrite($fp, $connect);
        $rsp = fread($fp, 1024);
        if (preg_match('/^HTTP/d.d 200/', $rsp) == 1) {
            return $fp;
        }
        echo "Request denied, $rspn";
        return false;
    }
    echo "Connection failed, $errno, $errstrn";
    return false;
}

This function returns a file pointer resource if the connection is successful, else FALSE. We can use that resource to communicate with the destination host.

$proxy = "138.204.48.233";
$port = 8080;
$destination = "api.ipify.org:80";
$fp = connect_to_http_proxy($proxy, $port, $destination);
if ($fp) {
    fwrite($fp, "GET /?format=json HTTP/1.1rnHost: $destinationrnrn");
    echo fread($fp, 1024);
    fclose($fp);
}

The communication protocol for SOCKS5 proxies is a little more complex:

  • The client connects to the proxy server and sends (at least) three bytes: The first byte is the SOCKS version, the second is the number of authentication methods, the next byte(s) is the authentication method(s).
  • The server responds with two bytes, the SOCKS version and the selected authentication method.
  • The client requests a connection to the destination host. The request contains the SOCKS version, followed by the command (CONNECT in this case), followed by a null byte. The fourth byte specifies the address type, and is followed by the address and port.
  • The server finally sends ten bytes (or seven or twenty-two, depending on the destination address type). The second byte contains the status and it should be zero, if the request is successful.
  • The server proxies all requests.

More details: SOCKS Protocol Version 5.

function connect_to_socks5_proxy($host, $port, $destination) {
    $fp = fsockopen($host, $port, $errno, $errstr);
    if ($errno == 0) {
        fwrite($fp, "510");
        $rsp = fread($fp, 2);
        if ($rsp === "50" ) {
            list($host, $port) = explode(":", $destination);
            $host = gethostbyname($host); //not required if $host is an IP
            $req = "5101" . inet_pton($host) . pack("n", $port);
            fwrite($fp, $req);
            $rsp = fread($fp, 10);
            if ($rsp[1] === "0") {
                return $fp;
            }
            echo "Request denied, status: " . ord($rsp[1]) . "n";
            return false;
        } 
        echo "Request deniedn";
        return false;
    }
    echo "Connection failed, $errno, $errstrn";
    return false;
}

This function works the same way as connect_to_http_proxy. Although both functions are tested, it would be best to use a library; the code is provided mostly for educational purposes.


SSL support and authentication.

We can't create an SSL connection with fsockopen using the ssl:// or tls:// protocol, because that would attempt to create an SSL connection with the proxy server, not the destination host. But it is possible to enable SSL with stream_socket_enable_crypto and create a secure communication channel with the destination, after the connenection with the proxy server has been established. This requires to disable peer verification, which can be done with stream_socket_client using a custom context. Note that disabling peer verification may be a security issue.

For HTTP proxies we can add authentication with the Proxy-Authenticate header. The value of this header is the authentication type, followed by the username and password, base64 encoded (Basic Authentication).

For SOCKS5 proxies the authentication process is - again - more complex. It seems we have to change the authentication code fron 0x00 (NO AUTHENTICATION REQUIRED) to 0x02 (USERNAME/PASSWORD authentication). It is not clear to me how to create a request with the authentication values, so I can not provide an example.

function connect_to_http_proxy($host, $port, $destination, $creds=null) {
    $context = stream_context_create(
        ['ssl'=> ['verify_peer'=> false, 'verify_peer_name'=> false]]
    );
    $soc = stream_socket_client(
        "tcp://$host:$port", $errno, $errstr, 20, 
        STREAM_CLIENT_CONNECT, $context
    );
    if ($errno == 0) {
        $auth = $creds ? "Proxy-Authorization: Basic ".base64_encode($creds)."rn": "";
        $connect = "CONNECT $destination HTTP/1.1rn$authrn";
        fwrite($soc, $connect);
        $rsp = fread($soc, 1024);
        if (preg_match('/^HTTP/d.d 200/', $rsp) == 1) {
            return $soc;
        }
        echo "Request denied, $rspn";
        return false;
    }
    echo "Connection failed, $errno, $errstrn";
    return false;
}

$host = "proxy IP";
$port = "proxy port";
$destination = "chat.freenode.net:6697";
$credentials = "user:pass";
$soc = connect_to_http_proxy($host, $port, $destination, $credentials);
if ($soc) {
    stream_socket_enable_crypto($soc, true, STREAM_CRYPTO_METHOD_ANY_CLIENT);
    fwrite($soc,"USER testnNICK testn");
    echo fread($soc, 1024);
    fclose($soc);
}
Saturday, May 29, 2021
 
Stefan
 
1

This is happening because you're not clearing the to addresses each time around your loop - notice the method is called addAddress, not setAddress, and it does what that suggests. You need to call $mail->clearAddresses(); at the end of your loop to reset it. You're doing things mostly right otherwise, but there are a few things you can do to improve efficiency, mainly using keep alive and setting all the properties that are common to all messages before your loop.

You can see all this working in the mailing list example provided with PHPMailer.

Saturday, May 29, 2021
 
vuliad
 
2

Rather than generating a possible database / storage for ~4 billion IP4 addresses (assuming you are only looking at IPv4 ignoring all IPv6 addresses) would it not be easier, and more practical just generate random IP address combinations to test with, which you can easily validate with RegEx?

The following regex would allow you to validate any particular IPv4 address from 0.0.0.0 to 255.255.255.255

b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)b

What you do need to remember however is that not all combinations of addresses are classed as 'valid' even if they are syntax correct.

Another alternative (as also mentioned by @binaryLV) is using the PHP filter_var($ip, FILTER_VALIDATE_IP); function which is nice and clean. Check the flags out to help filter too over at http://php.net/manual/en/function.filter-var.php

Update

For IP location based services I would recommend using a service such as IPInfoDB. They provide both an API (see here) to use which allows you to call their service reducing the need to store the information on your database. Else they offer a database of IP addresses (see here) to xxx.xxx.xxx precision that can also be downloaded.

Saturday, May 29, 2021
 
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :