| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Sisimai::SMTP::Transcript; |
|
2
|
4
|
|
|
4
|
|
4577
|
use v5.26; |
|
|
4
|
|
|
|
|
11
|
|
|
3
|
4
|
|
|
4
|
|
18
|
use strict; |
|
|
4
|
|
|
|
|
8
|
|
|
|
4
|
|
|
|
|
107
|
|
|
4
|
4
|
|
|
4
|
|
16
|
use warnings; |
|
|
4
|
|
|
|
|
18
|
|
|
|
4
|
|
|
|
|
206
|
|
|
5
|
4
|
|
|
4
|
|
288
|
use Sisimai::String; |
|
|
4
|
|
|
|
|
14
|
|
|
|
4
|
|
|
|
|
116
|
|
|
6
|
4
|
|
|
4
|
|
401
|
use Sisimai::SMTP::Reply; |
|
|
4
|
|
|
|
|
8
|
|
|
|
4
|
|
|
|
|
104
|
|
|
7
|
4
|
|
|
4
|
|
570
|
use Sisimai::SMTP::Status; |
|
|
4
|
|
|
|
|
8
|
|
|
|
4
|
|
|
|
|
93
|
|
|
8
|
4
|
|
|
4
|
|
293
|
use Sisimai::SMTP::Command; |
|
|
4
|
|
|
|
|
7
|
|
|
|
4
|
|
|
|
|
2918
|
|
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
sub rise { |
|
11
|
|
|
|
|
|
|
# Decode the transcript of the SMTP session and makes the structured data |
|
12
|
|
|
|
|
|
|
# @param [String] argv0 A transcript text MTA returned |
|
13
|
|
|
|
|
|
|
# @param [String] argv1 A label string of a SMTP client |
|
14
|
|
|
|
|
|
|
# @param [String] argv2 A label string of a SMTP server |
|
15
|
|
|
|
|
|
|
# @return [Array] Structured data |
|
16
|
|
|
|
|
|
|
# @return [undef] Failed to decode or the 1st argument is missing |
|
17
|
|
|
|
|
|
|
# @since v5.0.0 |
|
18
|
12
|
|
|
12
|
0
|
718
|
my $class = shift; |
|
19
|
12
|
|
100
|
|
|
36
|
my $argv0 = shift // return undef; |
|
20
|
11
|
|
100
|
|
|
28
|
my $argv1 = shift // '>>>'; # Label for an SMTP Client |
|
21
|
11
|
|
100
|
|
|
24
|
my $argv2 = shift // '<<<'; # Label for an SMTP Server |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
# 1. Replace label strings of SMTP client/server at the each line |
|
24
|
11
|
100
|
|
|
|
358
|
$argv0 =~ s/^[ ]+$argv1\s+/>>> /gm; return undef unless index($argv0, '>>> ') > -1; |
|
|
11
|
|
|
|
|
52
|
|
|
25
|
6
|
50
|
|
|
|
217
|
$argv0 =~ s/^[ ]+$argv2\s+/<<< /gm; return undef unless index($argv0, '<<< ') > -1; |
|
|
6
|
|
|
|
|
25
|
|
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# 2. Remove strings until the first '<<<' or '>>>' |
|
28
|
6
|
|
|
|
|
9
|
my $esmtp = []; |
|
29
|
|
|
|
|
|
|
my $table = sub { |
|
30
|
|
|
|
|
|
|
return { |
|
31
|
48
|
|
|
48
|
|
305
|
'command' => undef, # SMTP command |
|
32
|
|
|
|
|
|
|
'argument' => '', # An argument of each SMTP command sent from a client |
|
33
|
|
|
|
|
|
|
'parameter' => {}, # Parameter pairs of the SMTP command |
|
34
|
|
|
|
|
|
|
'response' => { # A Response from an SMTP server |
|
35
|
|
|
|
|
|
|
'reply' => '', # - SMTP reply code such as 550 |
|
36
|
|
|
|
|
|
|
'status' => '', # - SMTP status such as 5.1.1 |
|
37
|
|
|
|
|
|
|
'text' => [], # - Response text lines |
|
38
|
|
|
|
|
|
|
} |
|
39
|
|
|
|
|
|
|
}; |
|
40
|
6
|
|
|
|
|
28
|
}; |
|
41
|
|
|
|
|
|
|
|
|
42
|
6
|
|
|
|
|
12
|
my $cx = undef; # Current session for $esmtp |
|
43
|
6
|
|
|
|
|
11
|
my $p1 = index($argv0, '>>>'); # Sent command |
|
44
|
6
|
|
|
|
|
26
|
my $p2 = index($argv0, '<<<'); # Server response |
|
45
|
6
|
50
|
|
|
|
15
|
if( $p2 < $p1 ) { |
|
46
|
|
|
|
|
|
|
# An SMTP server response starting with '<<<' is the first |
|
47
|
6
|
|
|
|
|
23
|
push @$esmtp, $table->(); |
|
48
|
6
|
|
|
|
|
10
|
$cx = $esmtp->[-1]; |
|
49
|
6
|
|
|
|
|
14
|
$cx->{'command'} = 'CONN'; |
|
50
|
6
|
50
|
|
|
|
23
|
$argv0 = substr($argv0, $p2,) if $p2 > -1; |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
} else { |
|
53
|
|
|
|
|
|
|
# An SMTP command starting with '>>>' is the first |
|
54
|
0
|
0
|
|
|
|
0
|
$argv0 = substr($argv0, $p1,) if $p1 > -1; |
|
55
|
|
|
|
|
|
|
} |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
# 3. Remove unused lines, concatenate folded lines |
|
58
|
6
|
|
|
|
|
21
|
$argv0 = substr($argv0, 0, index($argv0, "\n\n") - 1); # Remove strings from the first blank line to the tail |
|
59
|
6
|
|
|
|
|
67
|
$argv0 =~ s/\n[ ]+/ /g; # Concatenate folded lines to each previous line |
|
60
|
|
|
|
|
|
|
|
|
61
|
6
|
|
|
|
|
49
|
for my $e ( split("\n", $argv0) ) { |
|
62
|
|
|
|
|
|
|
# 4. Read each SMTP command and server response |
|
63
|
162
|
100
|
|
|
|
300
|
if( index($e, '>>> ') == 0 ) { |
|
64
|
|
|
|
|
|
|
# SMTP client sent a command ">>> SMTP-command arguments" |
|
65
|
42
|
|
50
|
|
|
130
|
my $thecommand = Sisimai::SMTP::Command->find($e) || next; |
|
66
|
42
|
|
|
|
|
173
|
my $commandarg = Sisimai::String->sweep(substr($e, index($e, $thecommand) + length($thecommand),)); |
|
67
|
42
|
|
|
|
|
64
|
my $parameters = ''; |
|
68
|
|
|
|
|
|
|
|
|
69
|
42
|
|
|
|
|
65
|
push @$esmtp, $table->(); |
|
70
|
42
|
|
|
|
|
58
|
$cx = $esmtp->[-1]; |
|
71
|
42
|
|
|
|
|
76
|
$cx->{'command'} = uc $thecommand; |
|
72
|
|
|
|
|
|
|
|
|
73
|
42
|
100
|
100
|
|
|
200
|
if( $thecommand eq 'MAIL' || $thecommand eq 'RCPT' || $thecommand eq 'XFORWARD' ) { |
|
|
|
|
100
|
|
|
|
|
|
74
|
|
|
|
|
|
|
# MAIL or RCPT |
|
75
|
24
|
100
|
100
|
|
|
84
|
if( index($commandarg, 'FROM:') == 0 || index($commandarg, 'TO:') == 0 ) { |
|
76
|
|
|
|
|
|
|
# >>> MAIL FROM: SIZE=65535 |
|
77
|
|
|
|
|
|
|
# >>> RCPT TO: |
|
78
|
12
|
|
|
|
|
19
|
my $p4 = index($commandarg, '<'); |
|
79
|
12
|
|
|
|
|
26
|
my $p5 = index($commandarg, '>'); |
|
80
|
12
|
|
|
|
|
32
|
$cx->{'argument'} = substr($commandarg, $p4 + 1, $p5 - $p4 - 1); |
|
81
|
12
|
|
|
|
|
35
|
$parameters = Sisimai::String->sweep(substr($commandarg, $p5 + 1,)); |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
} else { |
|
84
|
|
|
|
|
|
|
# >>> XFORWARD NAME=neko2-nyaan3.y.example.co.jp ADDR=230.0.113.2 PORT=53672 |
|
85
|
|
|
|
|
|
|
# <<< 250 2.0.0 Ok |
|
86
|
|
|
|
|
|
|
# >>> XFORWARD PROTO=SMTP HELO=neko2-nyaan3.y.example.co.jp IDENT=2LYC6642BLzFK3MM SOURCE=REMOTE |
|
87
|
|
|
|
|
|
|
# <<< 250 2.0.0 Ok |
|
88
|
12
|
|
|
|
|
16
|
$parameters = $commandarg; |
|
89
|
12
|
|
|
|
|
15
|
$commandarg = ''; |
|
90
|
|
|
|
|
|
|
} |
|
91
|
|
|
|
|
|
|
|
|
92
|
24
|
|
|
|
|
54
|
for my $f ( split(" ", $parameters) ) { |
|
93
|
|
|
|
|
|
|
# SIZE=22022, PROTO=SMTP, and so on |
|
94
|
54
|
50
|
33
|
|
|
155
|
next if index($f, '=') < 1 || length $f < 3; |
|
95
|
|
|
|
|
|
|
|
|
96
|
54
|
50
|
|
|
|
87
|
my @ee = (split('=', $f)); next unless scalar @ee == 2; |
|
|
54
|
|
|
|
|
75
|
|
|
97
|
54
|
|
|
|
|
175
|
$cx->{'parameter'}->{ lc $ee[0] } = $ee[1]; |
|
98
|
|
|
|
|
|
|
} |
|
99
|
|
|
|
|
|
|
} else { |
|
100
|
|
|
|
|
|
|
# HELO, EHLO, AUTH, DATA, QUIT or Other SMTP command |
|
101
|
18
|
|
|
|
|
56
|
$cx->{'argument'} = $commandarg; |
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
} else { |
|
104
|
|
|
|
|
|
|
# SMTP server sent a response "<<< response text" |
|
105
|
120
|
50
|
|
|
|
134
|
my $p3 = index($e, '<<< '); next unless $p3 == 0; substr($e, $p3, 4, ''); |
|
|
120
|
|
|
|
|
160
|
|
|
|
120
|
|
|
|
|
150
|
|
|
106
|
|
|
|
|
|
|
|
|
107
|
120
|
|
50
|
|
|
281
|
$cx->{'response'}->{'reply'} = Sisimai::SMTP::Reply->find($e) || ''; |
|
108
|
120
|
|
100
|
|
|
288
|
$cx->{'response'}->{'status'} = Sisimai::SMTP::Status->find($e) || ''; |
|
109
|
120
|
|
|
|
|
252
|
push $cx->{'response'}->{'text'}->@*, $e; |
|
110
|
|
|
|
|
|
|
} |
|
111
|
|
|
|
|
|
|
} |
|
112
|
6
|
50
|
|
|
|
36
|
return undef unless scalar @$esmtp; |
|
113
|
6
|
|
|
|
|
47
|
return $esmtp; |
|
114
|
|
|
|
|
|
|
} |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
1; |
|
117
|
|
|
|
|
|
|
__END__ |