The format of data we are sending over the network will be flexible when sending...
[samba.git] / source3 / modules / vfs_smb_traffic_analyzer.c
1 /*
2  * traffic-analyzer VFS module. Measure the smb traffic users create
3  * on the net.
4  *
5  * Copyright (C) Holger Hetterich, 2008
6  * Copyright (C) Jeremy Allison, 2008
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include "includes.h"
23
24 /* abstraction for the send_over_network function */
25
26 enum sock_type {INTERNET_SOCKET = 0, UNIX_DOMAIN_SOCKET};
27
28 #define LOCAL_PATHNAME "/var/tmp/stadsocket"
29
30 /* VFS Functions identifier table. In protocol version 2, every vfs     */
31 /* function is given a unique id.                                       */
32 enum vfs_id {
33         /* care for the order here, required for compatibility  */
34         /* with protocol version 1.                             */
35         vfs_id_read,
36         vfs_id_pread,
37         vfs_id_write,
38         vfs_id_pwrite,
39         /* end of protocol version 1 identifiers.               */
40         vfs_id_mkdir
41 };
42
43 /* Specific data sets for the VFS functions.                            */
44
45 struct mkdir_data {
46         const char *path;
47         mode_t mode;
48         int result;
49 };
50
51 /* rw_data used for read/write/pread/pwrite                             */
52 struct rw_data {
53         char *filename;
54         size_t len;
55 };
56
57
58 static int vfs_smb_traffic_analyzer_debug_level = DBGC_VFS;
59
60 static enum sock_type smb_traffic_analyzer_connMode(vfs_handle_struct *handle)
61 {
62         connection_struct *conn = handle->conn;
63         const char *Mode;
64         Mode=lp_parm_const_string(SNUM(conn), "smb_traffic_analyzer","mode", \
65                         "internet_socket");
66         if (strstr(Mode,"unix_domain_socket")) {
67                 return UNIX_DOMAIN_SOCKET;
68         } else {
69                 return INTERNET_SOCKET;
70         }
71 }
72
73
74 /* Connect to an internet socket */
75
76 static int smb_traffic_analyzer_connect_inet_socket(vfs_handle_struct *handle,
77                                         const char *name, uint16_t port)
78 {
79         /* Create a streaming Socket */
80         int sockfd = -1;
81         struct addrinfo hints;
82         struct addrinfo *ailist = NULL;
83         struct addrinfo *res = NULL;
84         int ret;
85
86         ZERO_STRUCT(hints);
87         /* By default make sure it supports TCP. */
88         hints.ai_socktype = SOCK_STREAM;
89         hints.ai_flags = AI_ADDRCONFIG;
90
91         ret = getaddrinfo(name,
92                         NULL,
93                         &hints,
94                         &ailist);
95
96         if (ret) {
97                 DEBUG(3,("smb_traffic_analyzer_connect_inet_socket: "
98                         "getaddrinfo failed for name %s [%s]\n",
99                         name,
100                         gai_strerror(ret) ));
101                 return -1;
102         }
103
104         DEBUG(3,("smb_traffic_analyzer: Internet socket mode. Hostname: %s,"
105                 "Port: %i\n", name, port));
106
107         for (res = ailist; res; res = res->ai_next) {
108                 struct sockaddr_storage ss;
109                 NTSTATUS status;
110
111                 if (!res->ai_addr || res->ai_addrlen == 0) {
112                         continue;
113                 }
114
115                 ZERO_STRUCT(ss);
116                 memcpy(&ss, res->ai_addr, res->ai_addrlen);
117
118                 status = open_socket_out(&ss, port, 10000, &sockfd);
119                 if (NT_STATUS_IS_OK(status)) {
120                         break;
121                 }
122         }
123
124         if (ailist) {
125                 freeaddrinfo(ailist);
126         }
127
128         if (sockfd == -1) {
129                 DEBUG(1, ("smb_traffic_analyzer: unable to create "
130                         "socket, error is %s",
131                         strerror(errno)));
132                 return -1;
133         }
134
135         return sockfd;
136 }
137
138 /* Connect to a unix domain socket */
139
140 static int smb_traffic_analyzer_connect_unix_socket(vfs_handle_struct *handle,
141                                                 const char *name)
142 {
143         /* Create the socket to stad */
144         int len, sock;
145         struct sockaddr_un remote;
146
147         DEBUG(7, ("smb_traffic_analyzer_connect_unix_socket: "
148                         "Unix domain socket mode. Using %s\n",
149                         name ));
150
151         if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
152                 DEBUG(1, ("smb_traffic_analyzer_connect_unix_socket: "
153                         "Couldn't create socket, "
154                         "make sure stad is running!\n"));
155                 return -1;
156         }
157         remote.sun_family = AF_UNIX;
158         strlcpy(remote.sun_path, name,
159                     sizeof(remote.sun_path));
160         len=strlen(remote.sun_path) + sizeof(remote.sun_family);
161         if (connect(sock, (struct sockaddr *)&remote, len) == -1 ) {
162                 DEBUG(1, ("smb_traffic_analyzer_connect_unix_socket: "
163                         "Could not connect to "
164                         "socket, make sure\nstad is running!\n"));
165                 close(sock);
166                 return -1;
167         }
168         return sock;
169 }
170
171 /* Private data allowing shared connection sockets.     */
172
173 struct refcounted_sock {
174         struct refcounted_sock *next, *prev;
175         char *name;
176         uint16_t port;
177         int sock;
178         unsigned int ref_count;
179 };
180
181
182 /* The marshaller for the protocol version 2.           */
183 static char *smb_traffic_analyzer_create_string( struct tm *tm, \
184         int seconds, vfs_handle_struct *handle, \
185         char *username, int count, ... )
186 {
187         
188         va_list ap;
189         char *arg = NULL;
190         char *str = NULL;
191         int len;
192         char *header = NULL;
193         char *buf = NULL;
194         char *timestr = NULL;
195
196         /* first create the data that is transfered with any VFS op     */
197         len = strlen( username );
198         buf = talloc_asprintf(talloc_tos(),"%04u%s", len, username);
199         len = strlen( handle->conn->connectpath );
200         buf = talloc_asprintf_append( buf, "%04u%s", len, \
201                 handle->conn->connectpath );
202         len = strlen( pdb_get_domain(handle->conn->server_info->sam_account) );
203         buf = talloc_asprintf_append( buf, "%04u%s", len, \
204                 pdb_get_domain(handle->conn->server_info->sam_account) );
205         timestr = talloc_asprintf(talloc_tos(), \
206                 "%04d-%02d-%02d %02d:%02d:%02d.%03d", \
207                 tm->tm_year+1900, \
208                 tm->tm_mon+1, \
209                 tm->tm_mday, \
210                 tm->tm_hour, \
211                 tm->tm_min, \
212                 tm->tm_sec, \
213                 (int)seconds);
214         len = strlen( timestr );
215         buf = talloc_asprintf_append( buf, "%04u%s", len, timestr);
216         
217         va_start( ap, count );
218         while ( count-- ) {
219                 arg = va_arg( ap, char * );
220                 /* protocol v2 sends a four byte string */
221                 /* as a header to each block, including */
222                 /* the numbers of bytes to come in the  */
223                 /* next string.                         */
224                 len = strlen( arg );
225                 buf = talloc_asprintf_append( buf, "%04u%s", len, arg);
226         }
227         va_end( ap );
228
229         /* now create the protocol v2 header.   */
230         len = strlen( buf );
231         str = talloc_asprintf_append( str, "V2,%017u%s", len, buf);
232         DEBUG(10, ("smb_traffic_analyzer_create_string: %s\n",str));
233         return str;
234 }
235
236 static void smb_traffic_analyzer_send_data(vfs_handle_struct *handle,
237                                         void *data,
238                                         enum vfs_id vfs_operation )
239 {
240         struct refcounted_sock *rf_sock = NULL;
241         struct timeval tv;
242         time_t tv_sec;
243         struct tm *tm = NULL;
244         int seconds;
245         char *str = NULL;
246         char *username = NULL;
247         const char *anon_prefix = NULL;
248         const char *total_anonymization = NULL;
249         const char *protocol_version = NULL;
250         bool Write = false;
251         size_t len;
252
253         SMB_VFS_HANDLE_GET_DATA(handle, rf_sock, struct refcounted_sock, return);
254
255         if (rf_sock == NULL || rf_sock->sock == -1) {
256                 DEBUG(1, ("smb_traffic_analyzer_send_data: socket is "
257                         "closed\n"));
258                 return;
259         }
260
261         GetTimeOfDay(&tv);
262         tv_sec = convert_timespec_to_time_t(convert_timeval_to_timespec(tv));
263         tm = localtime(&tv_sec);
264         if (!tm) {
265                 return;
266         }
267         seconds=(float) (tv.tv_usec / 1000);
268
269         /* check if anonymization is required */
270
271         total_anonymization=lp_parm_const_string(SNUM(handle->conn),"smb_traffic_analyzer",
272                                         "total_anonymization", NULL);
273
274         anon_prefix=lp_parm_const_string(SNUM(handle->conn),"smb_traffic_analyzer",\
275                                         "anonymize_prefix", NULL );
276         if (anon_prefix!=NULL) {
277                 if (total_anonymization!=NULL) {
278                         username = talloc_asprintf(talloc_tos(),
279                                 "%s",
280                                 anon_prefix);
281                 } else {
282                         username = talloc_asprintf(talloc_tos(),
283                                 "%s%i",
284                                 anon_prefix,
285                                 str_checksum(
286                                         handle->conn->server_info->sanitized_username ) ); 
287                 }
288
289         } else {
290                 username = handle->conn->server_info->sanitized_username;
291         }
292
293         if (!username) {
294                 return;
295         }
296
297         protocol_version = lp_parm_const_string(SNUM(handle->conn),
298                 "smb_traffic_analyzer",
299                 "protocol_version", NULL );
300
301         if ( protocol_version == NULL || strcmp( protocol_version,"V1") == 0) {
302
303                 struct rw_data *s_data = (struct rw_data *) data;
304
305                 /* in case of protocol v1, ignore any vfs operations    */
306                 /* except read,pread,write,pwrite, and set the "Write"  */
307                 /* bool accordingly.                                    */
308
309                 if ( vfs_operation > vfs_id_pwrite ) return;
310
311                 if ( vfs_operation <= vfs_id_pread ) Write=false;
312                         else Write=true;
313
314                 str = talloc_asprintf(talloc_tos(),
315                         "V1,%u,\"%s\",\"%s\",\"%c\",\"%s\",\"%s\","
316                         "\"%04d-%02d-%02d %02d:%02d:%02d.%03d\"\n",
317                         (unsigned int) s_data->len,
318                         username,
319                         pdb_get_domain(handle->conn->server_info->sam_account),
320                         Write ? 'W' : 'R',
321                         handle->conn->connectpath,
322                         s_data->filename,
323                         tm->tm_year+1900,
324                         tm->tm_mon+1,
325                         tm->tm_mday,
326                         tm->tm_hour,
327                         tm->tm_min,
328                         tm->tm_sec,
329                         (int)seconds);
330         } else if ( strcmp( protocol_version, "V2") == 0) {
331
332                 switch( vfs_operation ) {
333                         case vfs_id_mkdir: ;
334                                 struct mkdir_data *s_data = \
335                                         (struct mkdir_data *) data;
336                                 str = smb_traffic_analyzer_create_string( tm, \
337                                         seconds, handle, username, \
338                                         3, s_data->path, \
339                                         talloc_asprintf( talloc_tos(), "%u", \
340                                                 s_data->mode), \
341                                         talloc_asprintf( talloc_tos(), "%u", \
342                                                 s_data->result ));
343                                 break;
344                         default:
345                                 DEBUG(1, ("smb_traffic_analyzer: error! "
346                                         "wrong VFS operation id detected!\n"));
347                                 return;
348                 }
349
350         } else {
351                 DEBUG(1, ("smb_traffic_analyzer_send_data_socket: "
352                         "error, unkown protocol given!\n"));
353                 return;
354         }
355
356         if (!str) {
357                 DEBUG(1, ("smb_traffic_analyzer_send_data: "
358                         "unable to create string to send!\n"));
359                 return;
360         }
361
362         len = strlen(str);
363
364         DEBUG(10, ("smb_traffic_analyzer_send_data_socket: sending %s\n",
365                         str));
366         if (write_data(rf_sock->sock, str, len) != len) {
367                 DEBUG(1, ("smb_traffic_analyzer_send_data_socket: "
368                         "error sending data to socket!\n"));
369                 return ;
370         }
371 }
372
373 static struct refcounted_sock *sock_list;
374
375 static void smb_traffic_analyzer_free_data(void **pptr)
376 {
377         struct refcounted_sock *rf_sock = *(struct refcounted_sock **)pptr;
378         if (rf_sock == NULL) {
379                 return;
380         }
381         rf_sock->ref_count--;
382         if (rf_sock->ref_count != 0) {
383                 return;
384         }
385         if (rf_sock->sock != -1) {
386                 close(rf_sock->sock);
387         }
388         DLIST_REMOVE(sock_list, rf_sock);
389         TALLOC_FREE(rf_sock);
390 }
391
392 static int smb_traffic_analyzer_connect(struct vfs_handle_struct *handle,
393                          const char *service,
394                          const char *user)
395 {
396         connection_struct *conn = handle->conn;
397         enum sock_type st = smb_traffic_analyzer_connMode(handle);
398         struct refcounted_sock *rf_sock = NULL;
399         const char *name = (st == UNIX_DOMAIN_SOCKET) ? LOCAL_PATHNAME :
400                                 lp_parm_const_string(SNUM(conn),
401                                         "smb_traffic_analyzer",
402                                 "host", "localhost");
403         uint16_t port = (st == UNIX_DOMAIN_SOCKET) ? 0 :
404                                 atoi( lp_parm_const_string(SNUM(conn),
405                                 "smb_traffic_analyzer", "port", "9430"));
406         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
407
408         if (ret < 0) {
409                 return ret;
410         }
411
412         /* Are we already connected ? */
413         for (rf_sock = sock_list; rf_sock; rf_sock = rf_sock->next) {
414                 if (port == rf_sock->port &&
415                                 (strcmp(name, rf_sock->name) == 0)) {
416                         break;
417                 }
418         }
419
420         /* If we're connected already, just increase the
421          * reference count. */
422         if (rf_sock) {
423                 rf_sock->ref_count++;
424         } else {
425                 /* New connection. */
426                 rf_sock = TALLOC_ZERO_P(NULL, struct refcounted_sock);
427                 if (rf_sock == NULL) {
428                         SMB_VFS_NEXT_DISCONNECT(handle);
429                         errno = ENOMEM;
430                         return -1;
431                 }
432                 rf_sock->name = talloc_strdup(rf_sock, name);
433                 if (rf_sock->name == NULL) {
434                         SMB_VFS_NEXT_DISCONNECT(handle);
435                         TALLOC_FREE(rf_sock);
436                         errno = ENOMEM;
437                         return -1;
438                 }
439                 rf_sock->port = port;
440                 rf_sock->ref_count = 1;
441
442                 if (st == UNIX_DOMAIN_SOCKET) {
443                         rf_sock->sock = smb_traffic_analyzer_connect_unix_socket(handle,
444                                                         name);
445                 } else {
446
447                         rf_sock->sock = smb_traffic_analyzer_connect_inet_socket(handle,
448                                                         name,
449                                                         port);
450                 }
451                 if (rf_sock->sock == -1) {
452                         SMB_VFS_NEXT_DISCONNECT(handle);
453                         TALLOC_FREE(rf_sock);
454                         return -1;
455                 }
456                 DLIST_ADD(sock_list, rf_sock);
457         }
458
459         /* Store the private data. */
460         SMB_VFS_HANDLE_SET_DATA(handle, rf_sock, smb_traffic_analyzer_free_data,
461                                 struct refcounted_sock, return -1);
462         return 0;
463 }
464
465 /* VFS Functions */
466
467 static int smb_traffic_analyzer_mkdir(vfs_handle_struct *handle, \
468                         const char *path, mode_t mode)
469 {
470         struct mkdir_data s_data;
471         s_data.result = SMB_VFS_NEXT_MKDIR(handle, path, mode);
472         s_data.path = path;
473         s_data.mode = mode;
474         DEBUG(10, ("smb_traffic_analyzer_mkdir: MKDIR: %s\n", path));
475         smb_traffic_analyzer_send_data(handle,
476                         &s_data,
477                         vfs_id_mkdir);
478         return s_data.result;
479 }
480
481 static ssize_t smb_traffic_analyzer_read(vfs_handle_struct *handle, \
482                                 files_struct *fsp, void *data, size_t n)
483 {
484         struct rw_data s_data;
485
486         s_data.len = SMB_VFS_NEXT_READ(handle, fsp, data, n);
487         s_data.filename = fsp->fsp_name->base_name;
488         DEBUG(10, ("smb_traffic_analyzer_read: READ: %s\n", fsp_str_dbg(fsp)));
489
490         smb_traffic_analyzer_send_data(handle,
491                         &s_data,
492                         vfs_id_read);
493         return s_data.len;
494 }
495
496
497 static ssize_t smb_traffic_analyzer_pread(vfs_handle_struct *handle, \
498                 files_struct *fsp, void *data, size_t n, SMB_OFF_T offset)
499 {
500         struct rw_data s_data;
501
502         s_data.len = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
503         s_data.filename = fsp->fsp_name->base_name;
504         DEBUG(10, ("smb_traffic_analyzer_pread: PREAD: %s\n",
505                    fsp_str_dbg(fsp)));
506
507         smb_traffic_analyzer_send_data(handle,
508                         &s_data,
509                         vfs_id_pread);
510
511         return s_data.len;
512 }
513
514 static ssize_t smb_traffic_analyzer_write(vfs_handle_struct *handle, \
515                         files_struct *fsp, const void *data, size_t n)
516 {
517         struct rw_data s_data;
518
519         s_data.len = SMB_VFS_NEXT_WRITE(handle, fsp, data, n);
520         s_data.filename = fsp->fsp_name->base_name;
521         DEBUG(10, ("smb_traffic_analyzer_write: WRITE: %s\n",
522                    fsp_str_dbg(fsp)));
523
524         smb_traffic_analyzer_send_data(handle,
525                         &s_data,
526                         vfs_id_write);
527         return s_data.len;
528 }
529
530 static ssize_t smb_traffic_analyzer_pwrite(vfs_handle_struct *handle, \
531              files_struct *fsp, const void *data, size_t n, SMB_OFF_T offset)
532 {
533         struct rw_data s_data;
534
535         s_data.len = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
536         s_data.filename = fsp->fsp_name->base_name;
537         DEBUG(10, ("smb_traffic_analyzer_pwrite: PWRITE: %s\n", \
538                 fsp_str_dbg(fsp)));
539
540         smb_traffic_analyzer_send_data(handle,
541                         &s_data,
542                         vfs_id_pwrite);
543         return s_data.len;
544 }
545
546 static struct vfs_fn_pointers vfs_smb_traffic_analyzer_fns = {
547         .connect_fn = smb_traffic_analyzer_connect,
548         .vfs_read = smb_traffic_analyzer_read,
549         .pread = smb_traffic_analyzer_pread,
550         .write = smb_traffic_analyzer_write,
551         .pwrite = smb_traffic_analyzer_pwrite,
552         .mkdir = smb_traffic_analyzer_mkdir
553 };
554
555 /* Module initialization */
556
557 NTSTATUS vfs_smb_traffic_analyzer_init(void)
558 {
559         NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
560                                         "smb_traffic_analyzer",
561                                         &vfs_smb_traffic_analyzer_fns);
562
563         if (!NT_STATUS_IS_OK(ret)) {
564                 return ret;
565         }
566
567         vfs_smb_traffic_analyzer_debug_level =
568                 debug_add_class("smb_traffic_analyzer");
569
570         if (vfs_smb_traffic_analyzer_debug_level == -1) {
571                 vfs_smb_traffic_analyzer_debug_level = DBGC_VFS;
572                 DEBUG(1, ("smb_traffic_analyzer_init: Couldn't register custom"
573                          "debugging class!\n"));
574         } else {
575                 DEBUG(3, ("smb_traffic_analyzer_init: Debug class number of"
576                         "'smb_traffic_analyzer': %d\n", \
577                         vfs_smb_traffic_analyzer_debug_level));
578         }
579
580         return ret;
581 }