SocketStream.php 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Mailer\Transport\Smtp\Stream;
  11. use Symfony\Component\Mailer\Exception\TransportException;
  12. /**
  13. * A stream supporting remote sockets.
  14. *
  15. * @author Fabien Potencier <fabien@symfony.com>
  16. * @author Chris Corbyn
  17. *
  18. * @internal
  19. */
  20. final class SocketStream extends AbstractStream
  21. {
  22. private $url;
  23. private $host = 'localhost';
  24. private $port = 465;
  25. private $timeout;
  26. private $tls = true;
  27. private $sourceIp;
  28. private $streamContextOptions = [];
  29. public function setTimeout(float $timeout): self
  30. {
  31. $this->timeout = $timeout;
  32. return $this;
  33. }
  34. public function getTimeout(): float
  35. {
  36. return $this->timeout ?? (float) ini_get('default_socket_timeout');
  37. }
  38. /**
  39. * Literal IPv6 addresses should be wrapped in square brackets.
  40. */
  41. public function setHost(string $host): self
  42. {
  43. $this->host = $host;
  44. return $this;
  45. }
  46. public function getHost(): string
  47. {
  48. return $this->host;
  49. }
  50. public function setPort(int $port): self
  51. {
  52. $this->port = $port;
  53. return $this;
  54. }
  55. public function getPort(): int
  56. {
  57. return $this->port;
  58. }
  59. /**
  60. * Sets the TLS/SSL on the socket (disables STARTTLS).
  61. */
  62. public function disableTls(): self
  63. {
  64. $this->tls = false;
  65. return $this;
  66. }
  67. public function isTLS(): bool
  68. {
  69. return $this->tls;
  70. }
  71. public function setStreamOptions(array $options): self
  72. {
  73. $this->streamContextOptions = $options;
  74. return $this;
  75. }
  76. public function getStreamOptions(): array
  77. {
  78. return $this->streamContextOptions;
  79. }
  80. /**
  81. * Sets the source IP.
  82. *
  83. * IPv6 addresses should be wrapped in square brackets.
  84. */
  85. public function setSourceIp(string $ip): self
  86. {
  87. $this->sourceIp = $ip;
  88. return $this;
  89. }
  90. /**
  91. * Returns the IP used to connect to the destination.
  92. */
  93. public function getSourceIp(): ?string
  94. {
  95. return $this->sourceIp;
  96. }
  97. public function initialize(): void
  98. {
  99. $this->url = $this->host.':'.$this->port;
  100. if ($this->tls) {
  101. $this->url = 'ssl://'.$this->url;
  102. }
  103. $options = [];
  104. if ($this->sourceIp) {
  105. $options['socket']['bindto'] = $this->sourceIp.':0';
  106. }
  107. if ($this->streamContextOptions) {
  108. $options = array_merge($options, $this->streamContextOptions);
  109. }
  110. // do it unconditionnally as it will be used by STARTTLS as well if supported
  111. $options['ssl']['crypto_method'] = $options['ssl']['crypto_method'] ?? \STREAM_CRYPTO_METHOD_TLS_CLIENT | \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT | \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
  112. $streamContext = stream_context_create($options);
  113. $timeout = $this->getTimeout();
  114. set_error_handler(function ($type, $msg) {
  115. throw new TransportException(sprintf('Connection could not be established with host "%s": ', $this->url).$msg);
  116. });
  117. try {
  118. $this->stream = stream_socket_client($this->url, $errno, $errstr, $timeout, \STREAM_CLIENT_CONNECT, $streamContext);
  119. } finally {
  120. restore_error_handler();
  121. }
  122. stream_set_blocking($this->stream, true);
  123. stream_set_timeout($this->stream, $timeout);
  124. $this->in = &$this->stream;
  125. $this->out = &$this->stream;
  126. }
  127. public function startTLS(): bool
  128. {
  129. return (bool) stream_socket_enable_crypto($this->stream, true);
  130. }
  131. public function terminate(): void
  132. {
  133. if (null !== $this->stream) {
  134. fclose($this->stream);
  135. }
  136. parent::terminate();
  137. }
  138. protected function getReadConnectionDescription(): string
  139. {
  140. return $this->url;
  141. }
  142. }