tcptee.pl 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. #!/usr/bin/perl
  2. # it is actually a 1-m tcp data distributor.
  3. use warnings;
  4. use strict;
  5. use IO::Socket;
  6. my $bidi;
  7. my $loop;
  8. my $myIp;
  9. my $myPort;
  10. my $ssl;
  11. my $serverHost;
  12. my $serverPort;
  13. my $usage = "Usage: tcptee.pl [--bidi] [--loop] [--ssl] " .
  14. "[myIp:]myPort[:serverHost:serverPort]\n";
  15. while(@ARGV) {
  16. my $opt = shift @ARGV;
  17. if($opt =~ m/^--bidi$/i) {
  18. $bidi = 1;
  19. } elsif($opt =~ m/^--loop$/i) {
  20. $loop = 1
  21. } elsif($opt =~ m/^--ssl$/i) {
  22. $ssl = 1
  23. } elsif($opt =~ m/^(\d+)$/) {
  24. $myPort = $opt;
  25. } elsif($opt =~ m/^(.*):(\d+):(.*):(\d+)$/) {
  26. $myIp = $1;
  27. $myPort = $2;
  28. $serverHost = $3;
  29. $serverPort = $4;
  30. } elsif($opt =~ m/^(\d+):(.*):(\d+)$/) {
  31. $myPort = $1;
  32. $serverHost = $2;
  33. $serverPort = $3;
  34. } else {
  35. die $usage;
  36. }
  37. }
  38. my ($sfd, $myfd, %clients, $discoMsg);
  39. die $usage if(!$myPort);
  40. sub
  41. tPrint($)
  42. {
  43. my $arg = shift;
  44. my @t = localtime;
  45. printf("%04d.%02d.%02d %02d:%02d:%02d %s\n",
  46. $t[5]+1900,$t[4]+1,$t[3], $t[2],$t[1],$t[0], $arg);
  47. }
  48. for(;;) {
  49. # Open the server first
  50. if($serverHost) {
  51. $sfd = IO::Socket::INET->new(PeerAddr => "$serverHost:$serverPort");
  52. if(!$sfd) {
  53. tPrint "Cannot connect to $serverHost:$serverPort : $!" if(!$discoMsg);
  54. $discoMsg = 1;
  55. last if(!$loop);
  56. sleep(5);
  57. next;
  58. }
  59. $discoMsg = 1;
  60. tPrint "Connected to $serverHost:$serverPort";
  61. }
  62. # Now open our listener
  63. $myfd = IO::Socket::INET->new(
  64. LocalHost => $myIp,
  65. LocalPort => $myPort,
  66. Listen => 10,
  67. ReuseAddr => 1
  68. );
  69. die "Opening port $myPort: $!\n" if(!$myfd);
  70. tPrint "Port $myPort opened";
  71. my $firstmsg; # HMLAN special
  72. # Data loop
  73. for(;;) {
  74. my ($rin,$rout) = ('','');
  75. vec($rin, $sfd->fileno(), 1) = 1 if($sfd);
  76. vec($rin, $myfd->fileno(), 1) = 1;
  77. foreach my $c (keys %clients) {
  78. vec($rin, fileno($clients{$c}{fd}), 1) = 1;
  79. }
  80. my $nfound = select($rout=$rin, undef, undef, undef);
  81. if($nfound < 0) {
  82. tPrint("select: $!");
  83. last;
  84. }
  85. # New connection
  86. if(vec($rout, $myfd->fileno(), 1)) {
  87. my @clientinfo = $myfd->accept();
  88. if(!@clientinfo) {
  89. tPrint "Accept failed: $!";
  90. next;
  91. }
  92. my ($port, $iaddr) = sockaddr_in($clientinfo[1]);
  93. my $fd = $clientinfo[0];
  94. $clients{$fd}{fd} = $fd;
  95. $clients{$fd}{addr} = inet_ntoa($iaddr) . ":$port";
  96. tPrint "Connection accepted from $clients{$fd}{addr}";
  97. if($ssl) {
  98. tPrint "Attaching SSL";
  99. eval "require IO::Socket::SSL";
  100. if($@) {
  101. tPrint "Can't load IO::Socket::SSL, falling back to plain";
  102. } else {
  103. my $ret = IO::Socket::SSL->start_SSL($fd, {
  104. SSL_server => 1,
  105. SSL_key_file => "certs/server-key.pem",
  106. SSL_cert_file => "certs/server-cert.pem",
  107. });
  108. if(!$ret && $! ne "Socket is not connected") {
  109. die "SSL/HTTPS error: $!";
  110. }
  111. }
  112. }
  113. syswrite($fd, $firstmsg) if($firstmsg);
  114. }
  115. # Data from the server
  116. if($sfd && vec($rout, $sfd->fileno(), 1)) {
  117. my $buf;
  118. my $ret = sysread($sfd, $buf, 256);
  119. if(!defined($ret) || $ret <= 0) {
  120. tPrint "Short read from the server, disconnecting the clients.";
  121. last;
  122. }
  123. foreach my $c (keys %clients) {
  124. syswrite($clients{$c}{fd}, $buf);
  125. $clients{$c}{fd}->flush();
  126. }
  127. $firstmsg = $buf if(!$firstmsg);
  128. }
  129. # Data from one of the clients
  130. CLIENT:foreach my $c (keys %clients) {
  131. next if(!vec($rout, fileno($clients{$c}{fd}), 1));
  132. for(;;) {
  133. my $buf;
  134. my $ret = sysread($clients{$c}{fd}, $buf, 256);
  135. if(!defined($ret) || $ret <= 0) {
  136. close($clients{$c}{fd});
  137. tPrint "Client $clients{$c}{addr} left us";
  138. delete($clients{$c});
  139. next CLIENT;
  140. }
  141. syswrite($sfd, $buf) if($sfd);
  142. if($bidi) {
  143. foreach my $c2 (keys %clients) {
  144. syswrite($clients{$c2}{fd}, $buf) if($c2 ne $c);
  145. }
  146. }
  147. last if(!$ssl || !$clients{$c}{fd}->pending());
  148. }
  149. }
  150. }
  151. close($sfd) if($sfd);
  152. close($myfd);
  153. foreach my $c (keys %clients) {
  154. close($clients{$c}{fd});
  155. delete($clients{$c});
  156. }
  157. last if(!$loop);
  158. sleep(1);
  159. }