-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathclamd.php
More file actions
168 lines (134 loc) · 5.14 KB
/
clamd.php
File metadata and controls
168 lines (134 loc) · 5.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
<?php
/* clamd.php v0.1 ClamAV daemon interface.
*
* Author : Barakat S. <b4r4k47@hotmail.com>
* Licence : MIT
*/
define('CLAMD_PIPE', '/var/run/clamav/clamd.ctl');
define('CLAMD_HOST', '127.0.0.1');
define('CLAMD_PORT', 3310);
define('CLAMD_MAXP', 20000);
/* EICAR is a simple test for AV scanners, see: https://en.wikipedia.org/wiki/EICAR_test_file */
$EICAR_TEST = 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*';
class ClamdSocketException extends Exception {
protected $errorCode;
public function __construct($message, $socketErrorCode) {
$this->errorCode = $socketErrorCode;
if (!$message) {
$message = socket_strerror($this->errorCode);
}
parent::__construct($message);
}
/* Get socket error (returned from 'socket_last_error') */
public function getErrorCode() {
return $this->errorCode;
}
}
/* An abstract class that `ClamdPipe` and `ClamdNetwork` will inherit. */
abstract class ClamdBase {
abstract protected function getSocket();
/* Send command to Clamd */
private function sendCommand($command) {
$return = null;
$socket = $this->getSocket();
socket_send($socket, $command, strlen($command), 0);
socket_recv($socket, $return, CLAMD_MAXP, 0);
socket_close($socket);
return $return;
}
/* `ping` command is used to see whether Clamd is alive or not */
public function ping() {
$return = $this->sendCommand('PING');
return strcmp($return, 'PONG') ? true : false;
}
/* `version` is used to receive the version of Clamd */
public function version() {
return trim($this->sendCommand('VERSION'));
}
/* `reload` Reload Clamd */
public function reload() {
return $this->sendCommand('RELOAD');
}
/* `shutdown` Shutdown Clamd */
public function shutdown() {
return $this->sendCommand('SHUTDOWN');
}
/* `fileScan` is used to scan single file. */
public function fileScan($file) {
list($file, $stats) = explode(':', $this->sendCommand('SCAN ' . $file));
return array( 'file' => $file, 'stats' => trim($stats));
}
/* `continueScan` is used to scan multiple files/directories. */
public function continueScan($file) {
$return = array();
foreach( explode("\n", trim($this->sendCommand('CONTSCAN ' . $file))) as $results ) {
list($file, $stats) = explode(':', $results);
array_push($return, array( 'file' => $file, 'stats' => trim($stats) ));
}
return $return;
}
/* `streamScan` is used to scan a buffer. */
public function streamScan($buffer) {
$port = null;
$socket = null;
$command = 'STREAM';
$return = null;
$socket = $this->getSocket();
socket_send($socket, $command, strlen($command), 0);
socket_recv($socket, $return, CLAMD_MAXP, 0);
sscanf($return, 'PORT %d\n', $port);
$stream = socket_create(AF_INET, SOCK_STREAM, 0);
socket_connect($stream, CLAMD_HOST, $port);
socket_send($stream, $buffer, strlen($buffer), 0);
socket_close($stream);
socket_recv($socket, $return, CLAMD_MAXP, 0);
socket_close($socket);
return array('stats' => trim(str_replace('stream: ', '', $return)));
}
}
/* This class can be used to connect to local socket, the default */
class ClamdPipe extends ClamdBase {
private $pip;
/* You need to pass the path to the socket pipe */
public function __construct($pip=CLAMD_PIPE) {
$this->pip = $pip;
}
protected function getSocket() {
$socket = @socket_create(AF_UNIX, SOCK_STREAM, 0);
if ($socket === FALSE) {
throw new ClamdSocketException('', socket_last_error());
}
$hasError = @socket_connect($socket, $this->pip);
if ($hasError === FALSE) {
$errorCode = socket_last_error();
$errorMessage = socket_strerror($errorCode);
if ($errorCode === 2) {
// ie. `No such file or directory "/var/run/clamav/clamd.ctl"`
$errorMessage .= ' "'.$this->pip.'", Is clamd running and are your user/group permissions configured properly?';
}
throw new ClamdSocketException($errorMessage, $errorCode);
}
return $socket;
}
}
/* This class can be used to connect to Clamd running over the network */
class ClamdNetwork extends ClamdBase {
private $host;
private $port;
/* You need to pass the host address and the port the the server */
public function __construct($host=CLAMD_HOST, $port=CLAMD_PORT) {
$this->host = $host;
$this->port = $port;
}
protected function getSocket() {
$socket = @socket_create(AF_INET, SOCK_STREAM, 0);
if ($socket === FALSE) {
throw new ClamdSocketException('', socket_last_error());
}
$hasError = @socket_connect($socket, $this->host, $this->port);
if ($hasError === FALSE) {
throw new ClamdSocketException('', socket_last_error());
}
return $socket;
}
}