|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace fkooman\VPN\Server\Log; |
|
4
|
|
|
|
|
5
|
|
|
use PDO; |
|
6
|
|
|
use RuntimeException; |
|
7
|
|
|
|
|
8
|
|
|
/** |
|
9
|
|
|
* With this we store events from client-connect and client-disconnect |
|
10
|
|
|
* from the OpenVPN server. |
|
11
|
|
|
*/ |
|
12
|
|
|
class ConnectionLog |
|
13
|
|
|
{ |
|
14
|
|
|
/** @var PDO */ |
|
15
|
|
|
private $db; |
|
16
|
|
|
|
|
17
|
|
|
/** @var string */ |
|
18
|
|
|
private $prefix; |
|
19
|
|
|
|
|
20
|
|
|
public function __construct(PDO $db, $prefix = '') |
|
21
|
|
|
{ |
|
22
|
|
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
|
23
|
|
|
$this->db = $db; |
|
24
|
|
|
$this->prefix = $prefix; |
|
25
|
|
|
} |
|
26
|
|
|
|
|
27
|
|
|
/** |
|
28
|
|
|
* Handle connecting client. |
|
29
|
|
|
* |
|
30
|
|
|
* @param array $v the environment variables from the connect event |
|
31
|
|
|
* |
|
32
|
|
|
* @throws RuntimeException if the insert fails |
|
33
|
|
|
*/ |
|
34
|
|
|
public function connect(array $v) |
|
35
|
|
|
{ |
|
36
|
|
|
$stmt = $this->db->prepare( |
|
37
|
|
|
sprintf( |
|
38
|
|
|
'INSERT INTO %s ( |
|
39
|
|
|
common_name, |
|
40
|
|
|
time_unix, |
|
41
|
|
|
v4, |
|
42
|
|
|
v6 |
|
43
|
|
|
) |
|
44
|
|
|
VALUES( |
|
45
|
|
|
:common_name, |
|
46
|
|
|
:time_unix, |
|
47
|
|
|
:v4, |
|
48
|
|
|
:v6 |
|
49
|
|
|
)', |
|
50
|
|
|
$this->prefix.'connections' |
|
51
|
|
|
) |
|
52
|
|
|
); |
|
53
|
|
|
|
|
54
|
|
|
$stmt->bindValue(':common_name', $v['common_name'], PDO::PARAM_STR); |
|
55
|
|
|
$stmt->bindValue(':time_unix', intval($v['time_unix']), PDO::PARAM_INT); |
|
56
|
|
|
$stmt->bindValue(':v4', $v['v4'], PDO::PARAM_STR); |
|
57
|
|
|
$stmt->bindValue(':v6', $v['v6'], PDO::PARAM_STR); |
|
58
|
|
|
$stmt->execute(); |
|
59
|
|
|
|
|
60
|
|
|
if (1 !== $stmt->rowCount()) { |
|
61
|
|
|
throw new RuntimeException('unable to insert'); |
|
62
|
|
|
} |
|
63
|
|
|
} |
|
64
|
|
|
|
|
65
|
|
|
/** |
|
66
|
|
|
* Handle disconnecting client. |
|
67
|
|
|
* |
|
68
|
|
|
* @param array $v the environment variables from the disconnect event |
|
69
|
|
|
* |
|
70
|
|
|
* @return bool whether the update succeeded, returns false if there is no |
|
71
|
|
|
* matching 'connect' in the database |
|
72
|
|
|
*/ |
|
73
|
|
|
public function disconnect(array $v) |
|
74
|
|
|
{ |
|
75
|
|
|
$stmt = $this->db->prepare( |
|
76
|
|
|
sprintf( |
|
77
|
|
|
'UPDATE %s |
|
78
|
|
|
SET bytes_received = :bytes_received, bytes_sent = :bytes_sent, disconnect_time_unix = :disconnect_time_unix |
|
79
|
|
|
WHERE common_name = :common_name |
|
80
|
|
|
AND time_unix = :time_unix |
|
81
|
|
|
AND v4 = :v4 |
|
82
|
|
|
AND v6 = :v6', |
|
83
|
|
|
$this->prefix.'connections' |
|
84
|
|
|
) |
|
85
|
|
|
); |
|
86
|
|
|
|
|
87
|
|
|
$stmt->bindValue(':common_name', $v['common_name'], PDO::PARAM_STR); |
|
88
|
|
|
$stmt->bindValue(':time_unix', intval($v['time_unix']), PDO::PARAM_INT); |
|
89
|
|
|
$stmt->bindValue(':v4', $v['v4'], PDO::PARAM_STR); |
|
90
|
|
|
$stmt->bindValue(':v6', $v['v6'], PDO::PARAM_STR); |
|
91
|
|
|
$stmt->bindValue(':bytes_received', intval($v['bytes_received']), PDO::PARAM_INT); |
|
92
|
|
|
$stmt->bindValue(':bytes_sent', intval($v['bytes_sent']), PDO::PARAM_INT); |
|
93
|
|
|
$stmt->bindValue(':disconnect_time_unix', intval($v['disconnect_time_unix']), PDO::PARAM_INT); |
|
94
|
|
|
$stmt->execute(); |
|
95
|
|
|
|
|
96
|
|
|
return 1 === $stmt->rowCount(); |
|
97
|
|
|
} |
|
98
|
|
|
|
|
99
|
|
|
public function getConnectionHistory($minUnix, $maxUnix) |
|
100
|
|
|
{ |
|
101
|
|
|
$stmt = $this->db->prepare( |
|
102
|
|
|
sprintf( |
|
103
|
|
|
'SELECT * |
|
104
|
|
|
FROM %s |
|
105
|
|
|
WHERE |
|
106
|
|
|
disconnect_time_unix NOT NULL |
|
107
|
|
|
AND |
|
108
|
|
|
disconnect_time_unix >= :min_unix |
|
109
|
|
|
AND |
|
110
|
|
|
disconnect_time_unix <= :max_unix |
|
111
|
|
|
ORDER BY disconnect_time_unix DESC', |
|
112
|
|
|
$this->prefix.'connections' |
|
113
|
|
|
) |
|
114
|
|
|
); |
|
115
|
|
|
$stmt->bindValue(':min_unix', $minUnix, PDO::PARAM_INT); |
|
116
|
|
|
$stmt->bindValue(':max_unix', $maxUnix, PDO::PARAM_INT); |
|
117
|
|
|
|
|
118
|
|
|
$stmt->execute(); |
|
119
|
|
|
|
|
120
|
|
|
return $stmt->fetchAll(PDO::FETCH_ASSOC); |
|
121
|
|
|
} |
|
122
|
|
|
|
|
123
|
|
|
public static function createTableQueries($prefix) |
|
124
|
|
|
{ |
|
125
|
|
|
$query = array( |
|
126
|
|
|
sprintf( |
|
127
|
|
|
'CREATE TABLE IF NOT EXISTS %s ( |
|
128
|
|
|
common_name VARCHAR(255) NOT NULL, |
|
129
|
|
|
time_unix INTEGER NOT NULL, |
|
130
|
|
|
v4 VARCHAR(255) NOT NULL, |
|
131
|
|
|
v6 VARCHAR(255) NOT NULL, |
|
132
|
|
|
bytes_received INTEGER DEFAULT NULL, |
|
133
|
|
|
bytes_sent INTEGER DEFAULT NULL, |
|
134
|
|
|
disconnect_time_unix INTEGER DEFAULT NULL |
|
135
|
|
|
)', |
|
136
|
|
|
$prefix.'connections' |
|
137
|
|
|
), |
|
138
|
|
|
); |
|
139
|
|
|
|
|
140
|
|
|
return $query; |
|
141
|
|
|
} |
|
142
|
|
|
|
|
143
|
|
|
/** |
|
144
|
|
|
* Remove all log entries older than provided timestamp. |
|
145
|
|
|
* |
|
146
|
|
|
* @param int $timeStamp the unix timestamp before which to remove all log entries |
|
147
|
|
|
*/ |
|
148
|
|
View Code Duplication |
public function housekeeping($timeStamp) |
|
|
|
|
|
|
149
|
|
|
{ |
|
150
|
|
|
$stmt = $this->db->prepare( |
|
151
|
|
|
sprintf( |
|
152
|
|
|
'DELETE FROM %s |
|
153
|
|
|
WHERE disconnect_time_unix < :time_stamp', |
|
154
|
|
|
$this->prefix.'connections' |
|
155
|
|
|
) |
|
156
|
|
|
); |
|
157
|
|
|
|
|
158
|
|
|
$stmt->bindValue(':time_stamp', $timeStamp, PDO::PARAM_INT); |
|
159
|
|
|
$stmt->execute(); |
|
160
|
|
|
|
|
161
|
|
|
return $stmt->rowCount(); |
|
162
|
|
|
} |
|
163
|
|
|
|
|
164
|
|
View Code Duplication |
public function initDatabase() |
|
|
|
|
|
|
165
|
|
|
{ |
|
166
|
|
|
$queries = self::createTableQueries($this->prefix); |
|
167
|
|
|
foreach ($queries as $q) { |
|
168
|
|
|
$this->db->query($q); |
|
169
|
|
|
} |
|
170
|
|
|
|
|
171
|
|
|
$tables = array('connections'); |
|
172
|
|
|
foreach ($tables as $t) { |
|
|
|
|
|
|
173
|
|
|
// make sure the tables are empty |
|
174
|
|
|
# $this->db->query( |
|
175
|
|
|
# sprintf( |
|
176
|
|
|
# 'DELETE FROM %s', |
|
177
|
|
|
# $this->prefix.$t |
|
178
|
|
|
# ) |
|
179
|
|
|
# ); |
|
180
|
|
|
} |
|
181
|
|
|
} |
|
182
|
|
|
} |
|
183
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.