1 /*
2  *    jnettop, network online traffic visualiser
3  *    Copyright (C) 2002-2006 Jakub Skopal
4  *
5  *    This program is free software; you can redistribute it and/or modify
6  *    it under the terms of the GNU General Public License as published by
7  *    the Free Software Foundation; either version 2 of the License, or
8  *    (at your option) any later version.
9  *
10  *    This program is distributed in the hope that it will be useful,
11  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *    GNU General Public License for more details.
14  *
15  *    You should have received a copy of the GNU General Public License
16  *    along with this program; if not, write to the Free Software
17  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  *    $Header: /cvsroot/jnettop/jnettop/juiadisplay.c,v 1.1 2006/04/11 15:21:06 merunka Exp $
20  *
21  */
22 
23 #include "jbase.h"
24 #include "jdevice.h"
25 #include "jprocessor.h"
26 #include "jconfig.h"
27 #include "jutil.h"
28 #include "juiadisplay.h"
29 
30 #ifdef ENABLE_UIA
31 
32 #define LISTEN_ERROR_ANSWER			"listen:ASCII:NAK:Error compiling rule: syntax error\n\n"
33 #define ERROR_MAXIMUM_TIMEOUT_EXPIRED		"\n\n"
34 #define GET_REQUEST_END_BOUNDARY		"\n"
35 
36 #define MAXRECV					32769
37 #define MAX_COMMAND_TIMEOUT_MINUTES		5
38 #define SMALL_WAIT				100
39 #define DEFAULT_LINE_COUNT			15
40 
41 GMutex		*displayStreamsMutex;
42 jbase_stream	**displayStreams = NULL;
43 int		displayStreamsCount = 0;
44 gboolean	bHaveData = FALSE;
45 int		nLineCount = DEFAULT_LINE_COUNT;
46 
47 gboolean	onoffPackets;
48 gboolean	onoffBitValues;
49 
processStreamsFunc(GPtrArray * streamArray)50 static void processStreamsFunc(GPtrArray * streamArray) {
51 	guint		i,j;
52 	guint		lines, oldLines;
53 	jbase_stream	**streams,**oldStreams;
54 
55 	streams = g_new0(jbase_stream *, nLineCount);
56 
57 	for (i=0,j=0; i<streamArray->len && j<nLineCount; i++) {
58 		jbase_stream *s = (jbase_stream *)g_ptr_array_index(streamArray, i);
59 		if (s->dead > 5) {
60 			continue;
61 		}
62 		s->displayed ++;
63 		streams[j++] = s;
64 	}
65 
66 	lines = j;
67 
68 	g_mutex_lock(displayStreamsMutex);
69 	if(lines > 0)
70 		bHaveData = TRUE;
71 	oldStreams = displayStreams;
72 	oldLines   = displayStreamsCount;
73 	displayStreams = streams;
74 	displayStreamsCount = lines;
75 	g_mutex_unlock(displayStreamsMutex);
76 
77 	for (i=0; i<oldLines; i++) {
78 		oldStreams[i]->displayed --;
79 	}
80 
81 	if (oldStreams)
82 		g_free(oldStreams);
83 }
84 
get_next_token_colon_delim(gchar ** text)85 static gchar * get_next_token_colon_delim(gchar ** text){
86 	gchar * tmp = NULL;
87 	if(!text)
88 		return NULL;
89 
90 	tmp = strsep(text, ":");
91 	if(tmp && *tmp != '\0'){
92 		return strdup(tmp);
93 	}else{
94 		return NULL;
95 	}
96 }
97 
doWriteFormatedNetworkStreams(pid_t nSessionID,gulong lUSecsWaited)98 void	doWriteFormatedNetworkStreams(pid_t nSessionID, gulong lUSecsWaited) {
99 	int i;
100 	gchar buffer[32768];
101 	gchar srcport[10], dstport[10], srcbps[10], dstbps[10], bps[10];
102 
103 	debug(LOG_DEBUG, "streams count %d", displayStreamsCount);
104 
105 	// dump out the totals line...
106 	jutil_formatNumber(onoffPackets?jprocessor_Stats.totalPPS:(onoffBitValues?8:1)*jprocessor_Stats.totalBPS, onoffPackets, bps, 6);
107 	g_strlcat(bps, "/s", sizeof(bps));
108 	jutil_formatNumber(onoffPackets?jprocessor_Stats.totalSrcPPS:(onoffBitValues?8:1)*jprocessor_Stats.totalSrcBPS, onoffPackets, srcbps, 6);
109 	g_strlcat(srcbps, "/s", sizeof(srcbps));
110 	jutil_formatNumber(onoffPackets?jprocessor_Stats.totalDstPPS:(onoffBitValues?8:1)*jprocessor_Stats.totalDstBPS, onoffPackets, dstbps, 6);
111 	g_strlcat(dstbps, "/s", sizeof(dstbps));
112 
113 	sprintf(buffer, "get:ASCII:%d:%d:ACK:TOTAL:::::%s:%s:%s\n", nSessionID, (int)lUSecsWaited, srcbps, dstbps, bps);
114 	//debug(LOG_DEBUG, "sending %d characters '%s'", strlen(buffer), buffer);
115 	printf("%s", buffer);
116 
117 	for (i=0; i< displayStreamsCount; i++) {
118 		gchar srcaddr[INET6_ADDRSTRLEN + 1], dstaddr[INET6_ADDRSTRLEN + 1];
119 		gchar total[10], totalsrc[10], totaldst[10];
120 		uint tmp;
121 		gchar linebuffer[1024];
122 		const gchar *psrcaddr, *pdstaddr;
123 		jbase_stream *s = displayStreams[i];
124 		tmp = onoffPackets ? s->totalpps : (onoffBitValues?8:1)*s->totalbps;
125 		jutil_formatNumber(tmp, onoffPackets, bps, 6);
126 		g_strlcat(bps, "/s", sizeof(bps));
127 		tmp = onoffPackets ? s->srcpps : (onoffBitValues?8:1)*s->srcbps;
128 		jutil_formatNumber(tmp, onoffPackets, srcbps, 6);
129 		g_strlcat(srcbps, "/s", sizeof(srcbps));
130 		tmp = onoffPackets ? s->dstpps : (onoffBitValues?8:1)*s->dstbps;
131 		jutil_formatNumber(tmp, onoffPackets, dstbps, 6);
132 		g_strlcat(dstbps, "/s", sizeof(dstbps));
133 		jutil_formatNumber(onoffPackets ? s->totalpackets : s->totalbytes, onoffPackets, total, 6);
134 		jutil_formatNumber(onoffPackets ? s->srcpackets : s->srcbytes, onoffPackets, totalsrc, 6);
135 		jutil_formatNumber(onoffPackets ? s->dstpackets : s->dstbytes, onoffPackets, totaldst, 6);
136 		jutil_Address2String(JBASE_AF(s->proto), &s->src, srcaddr, INET6_ADDRSTRLEN);
137 		if (s->srcresolv == NULL || s->srcresolv->name == NULL) {
138 			psrcaddr = srcaddr;
139 		} else {
140 			psrcaddr = s->srcresolv->name;
141 		}
142 		jutil_Address2String(JBASE_AF(s->proto), &s->dst, dstaddr, INET6_ADDRSTRLEN);
143 		if (s->dstresolv == NULL || s->dstresolv->name == NULL) {
144 			pdstaddr = dstaddr;
145 		} else {
146 			pdstaddr = s->dstresolv->name;
147 		}
148 		if (s->srcport == -1)
149 			strcpy(srcport, "AGGR.");
150 		else
151 			sprintf(srcport, "%d", s->srcport);
152 		if (s->dstport == -1)
153 			strcpy(dstport, "AGGR.");
154 		else
155 			sprintf(dstport, "%d", s->dstport);
156 		sprintf(linebuffer, "%s:%s", psrcaddr, pdstaddr);
157 
158 		sprintf(buffer, "get:ASCII:%d:%d:ACK:%s:%s:%s:%s:%s:%s:%s:%s\n", nSessionID, (int)lUSecsWaited, srcaddr, srcport, JBASE_PROTOCOLS[s->proto], dstaddr, dstport, srcbps, dstbps, bps);
159 		//debug(LOG_DEBUG, "sending %d characters '%s'", strlen(buffer), buffer);
160 		printf("%s", buffer);
161 	}
162 	printf(GET_REQUEST_END_BOUNDARY);
163 }
164 
timeNow()165 static GTimeVal timeNow(){
166 	GTimeVal timeNow;
167 	g_get_current_time(&timeNow);
168 	return timeNow;
169 }
170 
networkConnectionLoop()171 static void networkConnectionLoop(){
172 	// get our pid...
173 	pid_t ourpid = getpid();
174 	gboolean bExit = FALSE;
175 
176 	// setup timer here...
177 	GTimeVal	commandTimeout;
178 	g_get_current_time(&commandTimeout);
179 
180 	while(!bExit && (timeNow().tv_sec - commandTimeout.tv_sec < MAX_COMMAND_TIMEOUT_MINUTES * 60)){
181 		// make sure we don't block forever...
182 		int nSelectReturn = 0;
183 		fd_set listenSet;
184 		struct timeval tm;
185 		tm.tv_sec = 10;  // wait ten seconds...
186 		tm.tv_usec = 0;
187 		FD_ZERO(&listenSet);
188 		FD_SET(fileno(stdin), &listenSet);
189 
190 		nSelectReturn = select(fileno(stdin)+1, &listenSet, NULL, NULL, &tm);
191 
192 		if(nSelectReturn > 0){
193 			if(FD_ISSET(fileno(stdin), &listenSet)){
194 				int nDataRecievedCount = 0;
195  				gchar data[ MAXRECV + 1];
196 				bzero(data, MAXRECV);
197 				nDataRecievedCount = read(fileno(stdin), data, MAXRECV - 2);
198 
199 				if(nDataRecievedCount > 0){
200 					gchar * strPid = NULL;
201 					gchar * strType = NULL;
202 					gchar * strMethod = NULL;
203 					gchar * strMaxWaitUSecs = NULL;
204 					gchar * cpdata = data;
205 					int nIndex;
206 
207 					// make sure we end the string...
208 					data[nDataRecievedCount] = ':';
209 					data[nDataRecievedCount + 1] = '\0';
210 
211 					// remove any control charaters...
212 					for(nIndex = 0; nIndex<nDataRecievedCount; nIndex++){
213 						if(iscntrl(data[nIndex])){
214 							// this is a control character - change it to a :
215 							data[nIndex] = ':';
216 						}
217 					}
218 
219 					strMethod = get_next_token_colon_delim(&cpdata);
220 					strType = get_next_token_colon_delim(&cpdata);
221 					strPid = get_next_token_colon_delim(&cpdata);
222 					strMaxWaitUSecs = get_next_token_colon_delim(&cpdata);
223 
224 					if(strPid && ourpid != atoi(strPid)){
225 						// error - key id not correct
226 						debug(LOG_DEBUG, "Invalid session - %s given, %d expected", strPid, ourpid);
227 						printf(LISTEN_ERROR_ANSWER);
228 					}else if(strncmp(strMethod, "end", strlen("end")) == 0){
229 						gchar endAnswer[16384];
230 						g_get_current_time(&commandTimeout);
231 						// recived an end command
232 						sprintf(endAnswer, "end:ASCII:%d:ACK\n\n", ourpid);
233 						debug(LOG_DEBUG, "Sending '%s'", endAnswer);
234 						printf("%s", endAnswer);
235 						bExit = TRUE;
236 					}else if(strncmp(data, "get", strlen("get")) == 0){
237 						gulong lMicroSeconds = 0;
238 						gulong lWaitedSeconds = 0;
239 						g_get_current_time(&commandTimeout);
240 						// see how long we should wait...
241 
242 						if(strMaxWaitUSecs)
243 							lMicroSeconds = atol(strMaxWaitUSecs);
244 
245 
246 						debug(LOG_DEBUG, "waiting for data - will wait %d useconds", lMicroSeconds);
247 						while(!bHaveData && lWaitedSeconds < lMicroSeconds){
248 							lWaitedSeconds += SMALL_WAIT;
249 							g_usleep(SMALL_WAIT);
250 						}
251 
252 						debug(LOG_DEBUG, "waited %d useconds - bHaveData - %d", lWaitedSeconds, bHaveData);
253 
254 						if(bHaveData){
255 							// recieved get request
256 							// lock the mutex...
257 							g_mutex_lock(displayStreamsMutex);
258 							doWriteFormatedNetworkStreams(ourpid, lWaitedSeconds);
259 							g_mutex_unlock(displayStreamsMutex);
260 						}else{
261 							debug(LOG_DEBUG, "Timed out waiting for data - sending '%s'", ERROR_MAXIMUM_TIMEOUT_EXPIRED);
262 							printf(ERROR_MAXIMUM_TIMEOUT_EXPIRED);
263 						}
264 					}
265 					fflush(NULL);
266 				} // nDataRecievedCount
267 			} // if(FD_ISSET)
268 		} // select != -1
269 	} // while (!bExit)
270 	if(!(timeNow().tv_sec - commandTimeout.tv_sec  < MAX_COMMAND_TIMEOUT_MINUTES * 60)){
271 		// send timeout error
272 		debug(LOG_WARNING, "Timed out while waiting for get/end command - waited %d seconds", timeNow().tv_sec - commandTimeout.tv_sec);
273 		printf(ERROR_MAXIMUM_TIMEOUT_EXPIRED);
274 	}
275 }
276 
parseListenLineAndConfig()277 gboolean parseListenLineAndConfig(){
278 	// wait here for a listen command with parameters...
279 	gboolean bInitialized = FALSE;
280 
281 	static gchar data[ MAXRECV + 1];
282 
283 	pid_t ourpid = getpid();
284 	gboolean bBitValuesBackup = onoffBitValues;
285 	GTimeVal	commandTimeout;
286 
287 	// backup the device name
288 	gchar strDeviceBackup[30];
289 	strDeviceBackup[0] = '\0';
290 
291 	if(jconfig_Settings.deviceName)
292 		strcpy(strDeviceBackup, jconfig_Settings.deviceName);
293 
294 
295 	// setup timer here...
296 	g_get_current_time(&commandTimeout);
297 
298 
299 	// keep going while we have not initialized, aren't shutting down, and haven't timed out
300 	while(!bInitialized && (timeNow().tv_sec - commandTimeout.tv_sec < MAX_COMMAND_TIMEOUT_MINUTES * 60)){
301 		// make sure we don't block forever...
302 		fd_set listenSet;
303 		struct timeval tm;
304 		int nSelectReturn = 0;
305 		FD_ZERO(&listenSet);
306 		FD_SET(fileno(stdin), &listenSet);
307 		tm.tv_sec = 10;  // wait ten seconds...
308 		tm.tv_usec = 0;
309 		nSelectReturn = select(fileno(stdin)+1, &listenSet, NULL, NULL, &tm);
310 
311 		if(nSelectReturn != -1){
312 		// try to read stdout...
313 			if(FD_ISSET(fileno(stdin), &listenSet)){
314 				int nDataRecievedCount = read(fileno(stdin), data, MAXRECV - 2);
315 
316 				if(nDataRecievedCount > 0){
317 					int nIndex;
318 					gchar * strMethod = NULL;
319 					gchar * strType = NULL;
320 					gchar * strDevice = NULL;
321 					gchar * strBits = NULL;
322 					gchar * strFilter = NULL;
323 					gchar * strMaxLines = NULL;
324 					gchar * cpdata = data; //(gchar *) strdup(data);
325 
326 					// make sure we end the string...
327 					data[nDataRecievedCount] = ':';
328 					data[nDataRecievedCount+1] = '\0';
329 
330 					// clear the bpf filter...
331 					JCONFIG_BPFFILTERS_SETNONE;
332 					// reset the device name
333 					if(strlen(strDeviceBackup) > 0){
334 						strcpy(jconfig_Settings.deviceName, strDeviceBackup);
335 					}
336 					// reset the bit values
337 					onoffBitValues = bBitValuesBackup;
338 
339 					// remove any control charaters...
340 					for(nIndex = 0; nIndex<nDataRecievedCount; nIndex++){
341 						if(iscntrl(data[nIndex])){
342 							// this is a control character - change it to a :
343 							data[nIndex] = ':';
344 						}
345 					}
346 
347 					strMethod = get_next_token_colon_delim(&cpdata);
348 					strType = get_next_token_colon_delim(&cpdata);
349 					strDevice = get_next_token_colon_delim(&cpdata);
350 					strBits = get_next_token_colon_delim(&cpdata);
351 					strFilter = get_next_token_colon_delim(&cpdata);
352 					strMaxLines = get_next_token_colon_delim(&cpdata);
353 
354 					if(strncmp(strMethod, "listen", strlen("listen")) == 0){
355 						gchar firstAnswer[16384];
356 						int nStrMaxLines = 0;
357 						gboolean bIsRequestGood = TRUE;
358 
359 						if(strMaxLines)
360 							nStrMaxLines = atoi(strMaxLines);
361 
362 						debug(LOG_DEBUG, "Got listen request");
363 						// got a listen request
364 
365 
366 						if(strFilter){
367 							// set the filter...
368 							const char * strFilterResult = jutil_ValidateBPFFilter(strFilter);
369 							if(!strFilterResult){
370 								// good - set filter
371 								JCONFIG_BPFFILTERS_SETSELECTEDFILTER(JCONFIG_BPFFILTERS_LEN);
372 								jconfig_AddBpfFilter("<fromlisten>", strFilter);
373 							}else{
374 								debug(LOG_WARNING, "strFilter is BAD - %s", strFilterResult);
375 								bIsRequestGood = FALSE;
376 								printf(LISTEN_ERROR_ANSWER);
377 							}
378 						}
379 
380 						if(strDevice){
381 							debug(LOG_DEBUG, "Setting device name '%s'", strDevice);
382 							// set the device...
383 							jconfig_Settings.deviceName = strDevice;
384 						}
385 
386 						if(strBits && strcmp(strBits, "bits") == 0){
387 							debug(LOG_DEBUG, "Setting bits");
388 							// set bits...
389 							onoffBitValues = TRUE;
390 						}
391 
392 
393 						if(bIsRequestGood){
394 							if(nStrMaxLines != 0)
395 								nLineCount = nStrMaxLines;
396 
397 							sprintf(firstAnswer, "listen:ASCII:%d:ACK:%s:%s:%s:%s\n\n", ourpid, strDevice, strBits, strFilter, strMaxLines);
398 							debug(LOG_DEBUG,"sending '%s'", firstAnswer);
399 							printf(firstAnswer);
400 							bInitialized = TRUE;
401 						} else {
402 							printf(LISTEN_ERROR_ANSWER);
403 						}
404 						fflush(NULL);
405 						// reset the timeout timer..
406 						g_get_current_time(&commandTimeout);
407 					}
408 				} // if recv
409 			} // if FD_ISSET
410 		} // select
411 	} // while
412 	if(!(timeNow().tv_sec - commandTimeout.tv_sec < MAX_COMMAND_TIMEOUT_MINUTES * 60)){
413 		// send timeout error
414 		debug(LOG_NOTICE, "Timed out while waiting for listen command - waited %d seconds", timeNow().tv_sec - commandTimeout.tv_sec);
415 		printf(ERROR_MAXIMUM_TIMEOUT_EXPIRED);fflush(NULL);
416 	}
417 	return bInitialized;
418 }
419 
juiadisplay_PreSetup()420 static gboolean juiadisplay_PreSetup() {
421 	setvbuf(stdin, NULL, _IOLBF, 0);
422 	setvbuf(stdout, NULL, _IOLBF, 0);
423 	return parseListenLineAndConfig();
424 }
425 
juiadisplay_Setup()426 static void juiadisplay_Setup() {
427 	displayStreamsMutex = g_mutex_new();
428 
429 	jprocessor_SetProcessStreamsFunc((ProcessStreamsFunc) processStreamsFunc);
430 	onoffBitValues = FALSE;
431 	onoffPackets = FALSE;
432 }
433 
juiadisplay_PreRunSetup()434 static gboolean juiadisplay_PreRunSetup() {
435 	return TRUE;
436 }
437 
juiadisplay_PreRun()438 static void juiadisplay_PreRun() {
439 }
440 
juiadisplay_Run()441 static gboolean juiadisplay_Run() {
442 	networkConnectionLoop();
443 	return FALSE;
444 }
445 
juiadisplay_Shutdown()446 static void juiadisplay_Shutdown() {
447 }
448 
juiadisplay_DrawStatus(const gchar * msg)449 static void juiadisplay_DrawStatus(const gchar *msg) {
450 }
451 
juiadisplay_ProcessArgument(const gchar ** arg,int argc)452 static int juiadisplay_ProcessArgument(const gchar **arg, int argc) {
453 	if (!strcmp(*arg, "-b") || !strcmp(*arg, "--bit-units")) {
454 		onoffBitValues = TRUE;
455 		return 1;
456 	}
457 	return 0;
458 }
459 
460 jbase_display	juiadisplay_Functions = {
461 	TRUE,
462 	juiadisplay_PreSetup,
463 	juiadisplay_Setup,
464 	juiadisplay_PreRunSetup,
465 	juiadisplay_PreRun,
466 	juiadisplay_Run,
467 	juiadisplay_Shutdown,
468 	juiadisplay_DrawStatus,
469 	juiadisplay_ProcessArgument
470 };
471 
472 #else
473 
474 jbase_display	juiadisplay_Functions = { FALSE };
475 
476 #endif
477