UDPTransport.php 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. <?php
  2. /**
  3. * This program is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License along
  14. * with this program; if not, write to the Free Software Foundation, Inc.,
  15. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  16. * http://www.gnu.org/copyleft/gpl.html
  17. *
  18. * @file
  19. */
  20. /**
  21. * A generic class to send a message over UDP
  22. *
  23. * If a message prefix is provided to the constructor or via
  24. * UDPTransport::newFromString(), the payload of the UDP datagrams emitted
  25. * will be formatted with the prefix and a single space at the start of each
  26. * line. This is the payload format expected by the udp2log service.
  27. *
  28. * @since 1.25
  29. */
  30. class UDPTransport {
  31. private $host, $port, $prefix, $domain;
  32. /**
  33. * @param string $host IP address to send to
  34. * @param int $port port number
  35. * @param int $domain AF_INET or AF_INET6 constant
  36. * @param string|bool $prefix Prefix to use, false for no prefix
  37. */
  38. public function __construct( $host, $port, $domain, $prefix = false ) {
  39. $this->host = $host;
  40. $this->port = $port;
  41. $this->domain = $domain;
  42. $this->prefix = $prefix;
  43. }
  44. /**
  45. * @param string $info In the format of "udp://host:port/prefix"
  46. * @return UDPTransport
  47. * @throws InvalidArgumentException
  48. */
  49. public static function newFromString( $info ) {
  50. if ( preg_match( '!^udp:(?://)?\[([0-9a-fA-F:]+)\]:(\d+)(?:/(.*))?$!', $info, $m ) ) {
  51. // IPv6 bracketed host
  52. $host = $m[1];
  53. $port = intval( $m[2] );
  54. $prefix = $m[3] ?? false;
  55. $domain = AF_INET6;
  56. } elseif ( preg_match( '!^udp:(?://)?([a-zA-Z0-9.-]+):(\d+)(?:/(.*))?$!', $info, $m ) ) {
  57. $host = $m[1];
  58. if ( !IP::isIPv4( $host ) ) {
  59. $host = gethostbyname( $host );
  60. }
  61. $port = intval( $m[2] );
  62. $prefix = $m[3] ?? false;
  63. $domain = AF_INET;
  64. } else {
  65. throw new InvalidArgumentException( __METHOD__ . ': Invalid UDP specification' );
  66. }
  67. return new self( $host, $port, $domain, $prefix );
  68. }
  69. /**
  70. * @param string $text
  71. */
  72. public function emit( $text ) {
  73. // Clean it up for the multiplexer
  74. if ( $this->prefix !== false ) {
  75. $text = preg_replace( '/^/m', $this->prefix . ' ', $text );
  76. // Limit to 64KB
  77. if ( strlen( $text ) > 65506 ) {
  78. $text = substr( $text, 0, 65506 );
  79. }
  80. if ( substr( $text, -1 ) != "\n" ) {
  81. $text .= "\n";
  82. }
  83. } elseif ( strlen( $text ) > 65507 ) {
  84. $text = substr( $text, 0, 65507 );
  85. }
  86. $sock = socket_create( $this->domain, SOCK_DGRAM, SOL_UDP );
  87. if ( !$sock ) { // @todo should this throw an exception?
  88. return;
  89. }
  90. socket_sendto( $sock, $text, strlen( $text ), 0, $this->host, $this->port );
  91. socket_close( $sock );
  92. }
  93. }